添加对话体力与流量通知
authorluozheng <1732036298@qq.com>
Thu, 25 Sep 2025 13:37:48 +0000 (21:37 +0800)
committerluozheng <1732036298@qq.com>
Thu, 25 Sep 2025 13:37:48 +0000 (21:37 +0800)
code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h
code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h

index 19176de..4b4bd86 100644 (file)
@@ -57,6 +57,7 @@ typedef enum {
     HAND_SLEEP_MODE_START_NOTIFY, // 哄睡模式开始通知
     HAND_SLEEP_MODE_END_NOTIFY, // 哄睡模式结束通知
     HAND_DOWNLOAD_EMOJI_NOTIFY, // 下载表情通知
+    HAND_CHAT_STAMINA_NOTIFY, // 对话体力与流量通知
     MAX_NUM_HANDLER, 
 } UiServiceMsgId;
 
index e2d4136..db5c6f1 100644 (file)
@@ -90,6 +90,7 @@ static void HandTaskReminderNotify(Request *request);
 static void HandTaskCharacterConfigNotify(Request *request);
 static void HandSleepModeStartNotify(Request *request);
 static void HandSleepModeEndNotify(Request *request);
+static void HandChatStaminaNotify(Request *request);
 static void HandDownloadEmojiNotify(Request *request);
 // 消息处理程序项数组(每个消息类型对应一个处理函数,函数名以 Handle 开头)
 MsgHandlerEntry msgHandlers[] = {
@@ -99,6 +100,7 @@ MsgHandlerEntry msgHandlers[] = {
     {HAND_CHARACTER_CONFIG_CHANGE_NOTIFY,HandTaskCharacterConfigNotify},
     {HAND_SLEEP_MODE_START_NOTIFY,HandSleepModeStartNotify},
     {HAND_SLEEP_MODE_END_NOTIFY,HandSleepModeEndNotify},
+    {HAND_CHAT_STAMINA_NOTIFY,HandChatStaminaNotify},
     {HAND_DOWNLOAD_EMOJI_NOTIFY,HandDownloadEmojiNotify},
 };
 /* -------------------------------------------------------------------------- */
@@ -385,6 +387,7 @@ void HandSleepModeEndNotify(Request *request)
 {
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandSleepModeEndNotify msg_id = %d",request->msgId);
 }
+
 // 下载表情通知
 void HandDownloadEmojiNotify(Request *request)
 {
@@ -398,6 +401,21 @@ void HandDownloadEmojiNotify(Request *request)
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadEmojiNotify eyesColor = %s", eyesColor.c_str());
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadEmojiNotify eyesShape = %s", eyesShape.c_str());
 }
+
+// 对话体力与流量通知
+// 负载格式: "chatLeft|chatDesc|flowLeft|flowDesc|qrcodeUrl",其中可能有空字段
+static void HandChatStaminaNotify(Request *request)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandChatStaminaNotify msg_id = %d", request->msgId);
+    if (request->data == nullptr || request->len <= 0) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandChatStaminaNotify: invalid data");
+        return;
+    }
+    std::string payload(static_cast<char*>(request->data), request->len);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandChatStaminaNotify payload = %s", payload.c_str());
+    // TODO: UI 可在此解析并展示(或直接将payload转交ThunderBirdModel)
+}
+
 /* -------------------------------------------------------------------------- */
 #ifdef __cplusplus
 }
index 682adc2..d3448af 100644 (file)
@@ -651,7 +651,7 @@ static uint32_t ln_ota_get_task_at(uint8_t *para, uint32_t paraLen, char *strCom
 }
 
 /* 
- * 职责单一:仅触发“根据最近一次getTask结果下载并校验MD5”的动作
+ * 职责单一:仅触发"根据最近一次getTask结果下载并校验MD5"的动作
  * 不负责:查询任务/升级/重启/重取token
  */
 static uint32_t ln_ota_download_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
@@ -1470,6 +1470,73 @@ static uint32_t ln_download_emoji_at(uint8_t *para, uint32_t paraLen, char *strC
     free(json);
     return ERRCODE_SUCC;
 }
+/* AT^LN_GET_CHAT_STAMINA - 获取对话体力与流量信息 */
+static uint32_t ln_get_chat_stamina_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(para);
+    VAD_UNUSED(paraLen);
+    VAD_UNUSED(strCommonCmd);
+    const char *id = uapi_ln_get_device_id();
+    const char *token = uapi_ln_get_device_token();
+    if (!id || !id[0] || !token || !token[0]) {
+        printf("[LN][CHAT_STAMINA] deviceId/token missing\n");
+        return ERRCODE_FAIL;
+    }
+    uint32_t ret = ln_device_get_chat_stamina_http(id, token);
+    return ret;
+}
+
+/* AT^LN_CHAT_STAMINA_DEMO[=profile|chatLeft,flowLeft[,chatDesc,flowDesc,qrcodeUrl]]
+ * profile: member/enough/low/empty
+    预置场景:
+    AT^LN_CHAT_STAMINA_DEMO=member
+    AT^LN_CHAT_STAMINA_DEMO=enough
+    AT^LN_CHAT_STAMINA_DEMO=low
+    AT^LN_CHAT_STAMINA_DEMO=empty
+    自定义场景:
+    AT^LN_CHAT_STAMINA_DEMO=chatLeft,flowLeft[,chatDesc,flowDesc,qrcodeUrl]
+    例: AT^LN_CHAT_STAMINA_DEMO=10,80,"对话额度不足20轮,可扫码续充","流量不足100M了哦,扫码可充值流量","https://example.com/qrcode/recharge=860132063148589"
+ */
+static uint32_t ln_chat_stamina_demo_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    int chatLeft = 0;
+    int flowLeft = 0;
+    const char *chatDesc = NULL;
+    const char *flowDesc = NULL;
+    const char *qrcode = "https://example.com/qrcode/recharge";
+
+    if (para == NULL || paraLen == 0) {
+        printf("Usage: AT^LN_CHAT_STAMINA_DEMO=member|enough|low|empty or AT^LN_CHAT_STAMINA_DEMO=chatLeft,flowLeft[,chatDesc,flowDesc,qrcode]\n");
+        return ERRCODE_FAIL;
+    }
+
+    uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+    (void)ConvertCmdToParam(para, paraLen, argv, &argc);
+    if (argc >= 1) {
+        if (strcmp((char*)argv[0], "member") == 0) {
+            chatLeft = -1; chatDesc = NULL; flowLeft = 500; flowDesc = NULL;
+        } else if (strcmp((char*)argv[0], "enough") == 0) {
+            chatLeft = 25; chatDesc = NULL; flowLeft = 200; flowDesc = NULL;
+        } else if (strcmp((char*)argv[0], "low") == 0) {
+            chatLeft = 10; chatDesc = "对话额度不足20轮,可扫码续充"; flowLeft = 80; flowDesc = "流量不足100M了哦,扫码可充值流量";
+        } else if (strcmp((char*)argv[0], "empty") == 0) {
+            chatLeft = 0; chatDesc = "啊哦,对话轮数已用完,扫码续上吧"; flowLeft = 0; flowDesc = "流量用完了哦";
+        } else {
+            chatLeft = atoi((char*)argv[0]);
+            if (argc >= 2) flowLeft = atoi((char*)argv[1]); else flowLeft = 0;
+            if (argc >= 3) chatDesc = (char*)argv[2];
+            if (argc >= 4) flowDesc = (char*)argv[3];
+            if (argc >= 5) qrcode = (char*)argv[4];
+        }
+    }
+
+    push_chat_stamina_notification(chatLeft, chatDesc, flowLeft, flowDesc, qrcode);
+    printf("[LN][CHAT_STAMINA_DEMO] chatLeft=%d, flowLeft=%d\n", chatLeft, flowLeft);
+    return ERRCODE_SUCC;
+}
+
 static const at_cmd_table_t g_ln_api_cmd[] = {
     /* ----------------------------------- VAD ---------------------------------- */
     {(char *)"AT^VAD_INIT", vad_init, (char *)"初始化 VAD"},
@@ -1494,7 +1561,9 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_SET_DEV_USERID", ln_device_set_userid_at, (char *)"获取设备绑定用户ID(JSON,SET): AT^LN_SET_DEV_USERID"},
     {(char *)"AT^LN_OTA_GET_TASK", ln_ota_get_task_at, (char *)"获取OTA升级任务(JSON,GET): AT^LN_OTA_GET_TASK"},
     {(char *)"AT^LN_OTA_DOWNLOAD", ln_ota_download_at, (char *)"下载OTA升级包(JSON,GET->下载): AT^LN_OTA_DOWNLOAD"},
-   
+    {(char *)"AT^LN_GET_CHAT_STAMINA", ln_get_chat_stamina_at, (char *)"获取对话体力与流量: AT^LN_GET_CHAT_STAMINA"},
+    
+
      /* -------------------------------- WebSocket -------------------------------- */
     {(char *)"AT^LN_WS_START", ln_ws_start_at, (char *)"启动WebSocket连接: AT^LN_WS_START"},
     {(char *)"AT^LN_WS_STOP", ln_ws_stop_at, (char *)"停止WebSocket连接: AT^LN_WS_STOP"},
@@ -1526,6 +1595,7 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
        {(char *)"AT^LN_SLEEP_MODE_START", ln_sleep_mode_start_at, (char *)"哄睡模式开始: AT^LN_SLEEP_MODE_START[=storyTheme]"},
        {(char *)"AT^LN_SLEEP_MODE_END", ln_sleep_mode_end_at, (char *)"哄睡模式结束: AT^LN_SLEEP_MODE_END"},
     {(char *)"AT^LN_DOWNLOAD_EMOJI", ln_download_emoji_at, (char *)"下载表情: AT^LN_DOWNLOAD_EMOJI[=characterId,eyesColor,eyesShape]"},
+    {(char *)"AT^LN_CHAT_STAMINA_DEMO", ln_chat_stamina_demo_at, (char *)"演示对话体力/流量UI: AT^LN_CHAT_STAMINA_DEMO=member|enough|low|empty 或自定义"},
     /* 内存查看相关AT命令 */
     {(char *)"AT^LN_MEMORY_INFO", ln_memory_info_at, (char *)"查看系统内存信息: AT^LN_MEMORY_INFO"},
     {(char *)"AT^LN_FOLDER_SIZE", ln_folder_size_at, (char *)"查看文件夹大小: AT^LN_FOLDER_SIZE[=/path/to/folder]"},
index 0667ca1..e86d0b5 100644 (file)
@@ -38,6 +38,7 @@ typedef struct
 #define LN_STORY_FINISH_PATH "/v1/story/finishSleepMode"
 #define LN_DEVICE_GET_CHARACTER_PATH "/v1/device/getCharacter"
 #define LN_DEVICE_GET_USER_ID_PATH "/v1/device/getUserId"
+#define LN_DEVICE_GET_CHAT_STAMINA_PATH "/v1/device/getChatStamina"
 #define LN_OTA_GET_TASK_PATH "/v1/upgrade/getTask"
 #define LN_EMOJI_REPORT_PATH "/v1/emoji/reportEmoji"
 #define LN_EMOJI_REPORT_ALL_PATH "/v1/emoji/reportAllEmoji"
@@ -47,6 +48,7 @@ typedef struct
 #define LN_STORY_FINISH_URL LN_BASE_URL LN_STORY_FINISH_PATH
 #define LN_DEVICE_GET_CHARACTER_URL LN_BASE_URL LN_DEVICE_GET_CHARACTER_PATH
 #define LN_DEVICE_GET_USER_ID_URL LN_BASE_URL LN_DEVICE_GET_USER_ID_PATH
+#define LN_DEVICE_GET_CHAT_STAMINA_URL LN_BASE_URL LN_DEVICE_GET_CHAT_STAMINA_PATH
 #define LN_OTA_GET_TASK_URL LN_BASE_URL LN_OTA_GET_TASK_PATH
 // #define LN_OTA_GET_TASK_URL "http://110.42.224.129:8089/leiniaoota.jsp"
 #define LN_EMOJI_REPORT_URL LN_BASE_URL LN_EMOJI_REPORT_PATH
index 0b71a78..a75892f 100644 (file)
@@ -9,20 +9,25 @@ LnCharacterConfig g_ln_character_config = {0};
 /* 动态字符串工具 */
 static inline char *ln_strdup_s(const char *src)
 {
-    if (src == NULL) src = "";
+    if (src == NULL)
+        src = "";
     size_t len = strlen(src);
     char *p = (char *)malloc(len + 1);
-    if (!p) return NULL;
+    if (!p)
+        return NULL;
     memcpy(p, src, len + 1);
     return p;
 }
 
 static inline void ln_assign_dup(char **dst, const char *src)
 {
-    if (!dst) return;
+    if (!dst)
+        return;
     char *newp = ln_strdup_s(src);
-    if (!newp) return;
-    if (*dst) free(*dst);
+    if (!newp)
+        return;
+    if (*dst)
+        free(*dst);
     *dst = newp;
 }
 
@@ -54,12 +59,12 @@ void ln_read_character_config(void)
 static void ln_save_character_config(const char *json_buf)
 {
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] save character config");
-    if (!json_buf) 
+    if (!json_buf)
     {
         printf("[DEVICE_CHARACTER] json_buf is NULL\n");
         return;
     }
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LN_CHARACTER_CONFIG_FILE:%s",LN_CHARACTER_CONFIG_FILE);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LN_CHARACTER_CONFIG_FILE:%s", LN_CHARACTER_CONFIG_FILE);
     FILE *file = fopen(LN_CHARACTER_CONFIG_FILE, "wb+");
     if (!file)
     {
@@ -204,7 +209,7 @@ void ln_parse_character_config(const char *json_buf, int json_len)
         g_ln_character_config.avatar_screen_image_as_emoji = 0;
     }
 
-    //将json体落库
+    // 将json体落库
     ln_save_character_config(text);
 
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] push character config change notification");
@@ -222,12 +227,12 @@ void ln_parse_character_config(const char *json_buf, int json_len)
 
     /* 推送角色配置更改通知 */
     push_character_config_change_notification(g_ln_character_config.character_id,
-        g_ln_character_config.character_name,
-        g_ln_character_config.avatar_hair_color,
-        g_ln_character_config.avatar_eyes_color,
-        g_ln_character_config.avatar_hair_style,
-        g_ln_character_config.avatar_eyes_shape,
-        g_ln_character_config.avatar_screen_image_as_emoji);
+                                              g_ln_character_config.character_name,
+                                              g_ln_character_config.avatar_hair_color,
+                                              g_ln_character_config.avatar_eyes_color,
+                                              g_ln_character_config.avatar_hair_style,
+                                              g_ln_character_config.avatar_eyes_shape,
+                                              g_ln_character_config.avatar_screen_image_as_emoji);
 
     cJSON_Delete(root);
     free(text);
@@ -510,3 +515,174 @@ uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_us
     free(header_buf3);
     return ERRCODE_SUCC;
 }
+
+// 获取对话体力与流量信息
+uint32_t ln_device_get_chat_stamina_http(const char *device_id, const char *device_token)
+{
+    if (!device_id || !device_token)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] missing deviceId/token");
+        return ERRCODE_FAIL;
+    }
+
+#ifdef SUPPORT_CAT1_SERVICE
+    cat1_api_add_cat1_vote();
+    cat1_api_wakeup_cat1(1);
+    cat1_api_keep_wakeup(1);
+    cat1_api_disable_cat1_cmd(0);
+    LOS_Msleep(200);
+#endif
+
+    HttpClient client = (HttpClient){0};
+    HttpClientData clientData = (HttpClientData){0};
+    client.isHttp = false;
+    client.remotePort = 443;
+
+    char *header_buf = (char *)malloc(512);
+    if (!header_buf)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] malloc failed for header_buf");
+        return ERRCODE_FAIL;
+    }
+    int hdr = snprintf(header_buf, 512,
+                       "deviceId: %s\r\n"
+                       "deviceToken: %s\r\n"
+                       "Content-Type: application/json\r\n",
+                       device_id, device_token);
+    if (hdr <= 0)
+    {
+        free(header_buf);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] snprintf failed for header_buf");
+        return ERRCODE_FAIL;
+    }
+    HttpClientSetCustomHeader(&client, header_buf);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] header_buf: %s", header_buf);
+
+    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE * 2);
+    if (prep != HTTP_SUCCESS)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] prepare failed: %d", prep);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    HTTPC_RESULT res = HttpClientGetRequest(&client, LN_DEVICE_GET_CHAT_STAMINA_URL, &clientData);
+    if (res != HTTP_SUCCESS)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] request failed: %d", res);
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    int code = HttpClientGetResponseCode(&client);
+    int body_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
+    if (body_len <= 0 || !clientData.responseBuf)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] empty body, code=%d", code);
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+    if (clientData.responseBuf && clientData.responseBufLen > 0)
+    {
+        int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
+        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");
+            free(header_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);
+    }
+
+    char *json_text = (char *)malloc((size_t)body_len + 1);
+    if (!json_text)
+    {
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+    memcpy(json_text, clientData.responseBuf, (size_t)body_len);
+    json_text[body_len] = '\0';
+
+    /* 若非200,打印原始响应体便于排查 */
+    if (code != 200) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] HTTP code=%d", code);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] body=%s", json_text);
+    }
+
+    cJSON *root = cJSON_Parse(json_text);
+    if (!root)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] json parse failed");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] raw body: %s", json_text);
+        free(json_text);
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode");
+    if (errorCode && !cJSON_IsNull(errorCode))
+    {
+        cJSON *message = cJSON_GetObjectItem(root, "message");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] server error: %s, msg=%s",
+                      cJSON_IsString(errorCode) ? errorCode->valuestring : "<unknown>",
+                      (message && cJSON_IsString(message)) ? message->valuestring : "<none>");
+        cJSON_Delete(root);
+        free(json_text);
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    cJSON *data = cJSON_GetObjectItem(root, "data");
+    if (!data || !cJSON_IsObject(data))
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] no data");
+        cJSON_Delete(root);
+        free(json_text);
+        HttpClientUnprepare(&clientData);
+        HttpClientClse(&client);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+
+    cJSON *chatLeft = cJSON_GetObjectItem(data, "chatStaminaLeft");
+    cJSON *chatDesc = cJSON_GetObjectItem(data, "chatStaminaDesc");
+    cJSON *flowLeft = cJSON_GetObjectItem(data, "cardFlowLeft");
+    cJSON *flowDesc = cJSON_GetObjectItem(data, "cardFlowDesc");
+    cJSON *qrcode = cJSON_GetObjectItem(data, "qrcodeUrl");
+
+    int chat_left = chatLeft && cJSON_IsNumber(chatLeft) ? (int)chatLeft->valuedouble : 0;
+    const char *chat_desc = (chatDesc && cJSON_IsString(chatDesc)) ? chatDesc->valuestring : NULL;
+    int flow_left = flowLeft && cJSON_IsNumber(flowLeft) ? (int)flowLeft->valuedouble : 0;
+    const char *flow_desc = (flowDesc && cJSON_IsString(flowDesc)) ? flowDesc->valuestring : NULL;
+    const char *qr = (qrcode && cJSON_IsString(qrcode)) ? qrcode->valuestring : NULL;
+
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] chatStaminaLeft=%d, chatStaminaDesc=%s",
+                  chat_left, chat_desc ? chat_desc : "<null>");
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] cardFlowLeft=%d, cardFlowDesc=%s",
+                  flow_left, flow_desc ? flow_desc : "<null>");
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[CHAT_STAMINA] qrcodeUrl=%s", qr ? qr : "<null>");
+
+    /* 推送到UI */
+    push_chat_stamina_notification(chat_left, chat_desc, flow_left, flow_desc, qr);
+
+    cJSON_Delete(root);
+    free(json_text);
+    HttpClientUnprepare(&clientData);
+    HttpClientClse(&client);
+    free(header_buf);
+    return ERRCODE_SUCC;
+}
\ No newline at end of file
index 4277f7a..2022490 100644 (file)
@@ -447,27 +447,6 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     free(text);
 }
 
-static inline int ln_fill_auth_header(HttpClient *client,
-                                      const char *device_id,
-                                      const char *device_token)
-{
-    if (!client || !device_id || !device_token)
-        return -1;
-    char *header_buf = (char *)malloc(512);
-    if (!header_buf)
-        return -1;
-    int hdr = snprintf(header_buf, 512,
-                       "deviceId: %s\r\n"
-                       "deviceToken: %s\r\n"
-                       "Content-Type: application/json\r\n",
-                       device_id, device_token);
-    if (hdr <= 0)
-        return -1;
-    HttpClientSetCustomHeader(client, header_buf);
-    free(header_buf);
-    return 0;
-}
-
 /* 7.1 上报角色表情组下载状态 */
 uint32_t ln_emoji_report_status_http(const char *device_id,
                                      const char *device_token,
@@ -495,11 +474,25 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     HttpClient client = (HttpClient){0};
     HttpClientData clientData = (HttpClientData){0};
 
-    if (ln_fill_auth_header(&client, device_id, device_token) != 0)
+    /* 直接构造自定义请求头,生命周期贯穿整个请求,结束后再释放 */
+    char *header_buf = (char *)malloc(512);
+    if (!header_buf)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] fill auth header failed");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc header_buf failed");
         return ERRCODE_FAIL;
     }
+    int hdr = snprintf(header_buf, 512,
+                       "deviceId: %s\r\n"
+                       "deviceToken: %s\r\n"
+                       "Content-Type: application/json\r\n",
+                       device_id, device_token);
+    if (hdr <= 0)
+    {
+        free(header_buf);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] build header failed");
+        return ERRCODE_FAIL;
+    }
+    HttpClientSetCustomHeader(&client, header_buf);
 
     /* 组装 JSON */
     cJSON *root = cJSON_CreateObject();
@@ -524,6 +517,7 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     if (prep != HTTP_SUCCESS)
     {
         free(body);
+        free(header_buf);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] prepare auto failed");
         return ERRCODE_FAIL;
     }
@@ -544,6 +538,7 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     {
         free(body);
         HttpClientUnprepare(&clientData);
+        free(header_buf);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] request failed");
         return ERRCODE_FAIL;
     }
@@ -573,12 +568,14 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     {
         free(body);
         HttpClientUnprepare(&clientData);
+        free(header_buf);
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
         return ERRCODE_FAIL;
     }
 
     free(body);
     HttpClientUnprepare(&clientData);
+    free(header_buf);
     return ERRCODE_SUCC;
 }
 /* 7.2 上报设备所有表情组状态 */
@@ -603,8 +600,21 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
     client.isHttp = false;
     client.remotePort = 443;
 
-    if (ln_fill_auth_header(&client, device_id, device_token) != 0)
+    /* 直接构造自定义请求头,生命周期贯穿整个请求,结束后再释放 */
+    char *header_buf = (char *)malloc(512);
+    if (!header_buf)
         return ERRCODE_FAIL;
+    int hdr = snprintf(header_buf, 512,
+                       "deviceId: %s\r\n"
+                       "deviceToken: %s\r\n"
+                       "Content-Type: application/json\r\n",
+                       device_id, device_token);
+    if (hdr <= 0)
+    {
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
+    HttpClientSetCustomHeader(&client, header_buf);
 
     /* 组装 JSON 数组 */
     cJSON *root = cJSON_CreateObject();
@@ -639,6 +649,7 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
     if (prep != HTTP_SUCCESS)
     {
         free(body);
+        free(header_buf);
         return ERRCODE_FAIL;
     }
     clientData.postBuf = body;
@@ -650,6 +661,7 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
     {
         free(body);
         HttpClientUnprepare(&clientData);
+        free(header_buf);
         return ERRCODE_FAIL;
     }
 
@@ -672,11 +684,13 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
     {
         free(body);
         HttpClientUnprepare(&clientData);
+        free(header_buf);
         return ERRCODE_FAIL;
     }
 
     free(body);
     HttpClientUnprepare(&clientData);
+    free(header_buf);
     return ERRCODE_SUCC;
 }
 
index cd97552..aea9993 100644 (file)
@@ -149,6 +149,23 @@ void push_download_emoji_notification(char *characterId, char *eyesColor, char *
     //切换表情通知ui
     SendMsgToUIService(HAND_DOWNLOAD_EMOJI_NOTIFY, 0, (void *)payload, (int32_t)strlen(payload));
 }
+// 推送对话体力与流量信息到UI
+// chatLeft:对话体力剩余数量
+// chatDesc:对话体力说明文字
+// flowLeft:流量剩余数量
+// flowDesc:流量说明文字
+// qrcodeUrl:充值二维码链接
+void push_chat_stamina_notification(int chatLeft, const char *chatDesc,
+                                    int flowLeft, const char *flowDesc,
+                                    const char *qrcodeUrl)
+{
+    char payload[512] = {0};
+    const char *cd = chatDesc ? chatDesc : "";
+    const char *fd = flowDesc ? flowDesc : "";
+    const char *qr = qrcodeUrl ? qrcodeUrl : "";
+    (void)snprintf(payload, sizeof(payload) - 1, "%d|%s|%d|%s|%s", chatLeft, cd, flowLeft, fd, qr);
+    SendMsgToUIService(HAND_CHAT_STAMINA_NOTIFY, 0, (void *)payload, (int32_t)strlen(payload));
+}
 #ifdef __cplusplus
 };
 #endif
\ No newline at end of file
index b2b090c..4ca2875 100644 (file)
@@ -67,6 +67,12 @@ void push_sleep_mode_start_notification(const char *storyTheme);
 void push_sleep_mode_end_notification(void);
 //通知ui下载表情
 void push_download_emoji_notification(char *characterId, char *eyesColor, char *eyesShape);
+// 推送对话体力与流量信息到UI(管道字符串,以'|'分隔)
+void push_chat_stamina_notification(int chatLeft, 
+                                    const char *chatDesc,
+                                    int flowLeft, 
+                                    const char *flowDesc,
+                                    const char *qrcodeUrl);
 #ifdef __cplusplus
 }
 #endif