From e83568f5fb953e86abfe08e8ecdf585c66a9758a Mon Sep 17 00:00:00 2001 From: xiao12feng8 <16507319+xiao12feng8@user.noreply.gitee.com> Date: Tue, 30 Dec 2025 11:11:07 +0800 Subject: [PATCH] =?UTF-8?q?bug=EF=BC=9A=E4=BF=AE=E5=A4=8D=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E4=B8=8D=E8=83=BD=E6=92=A4=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Zhibo/admin/src/api/session.js | 20 + Zhibo/admin/src/views/session/list/index.vue | 676 +++++++++++++++--- .../admin/controller/SessionController.java | 300 +++++++- .../common/model/chat/PrivateMessage.java | 8 + .../zbkj/front/controller/CallController.java | 1 + .../controller/ConversationController.java | 11 + .../service/impl/ConversationServiceImpl.java | 4 +- Zhibo/zhibo-h/sql/add_recall_fields.sql | 8 + .../livestreaming/ConversationActivity.java | 79 +- .../main/res/layout/activity_conversation.xml | 7 +- 10 files changed, 951 insertions(+), 163 deletions(-) create mode 100644 Zhibo/zhibo-h/sql/add_recall_fields.sql diff --git a/Zhibo/admin/src/api/session.js b/Zhibo/admin/src/api/session.js index 20494afd..7a7f34ff 100644 --- a/Zhibo/admin/src/api/session.js +++ b/Zhibo/admin/src/api/session.js @@ -10,7 +10,27 @@ export function sessionDetailApi(id) { return request({ url: '/admin/session/detail/' + id, method: 'get' }) } +// 会话消息列表 +export function sessionMessagesApi(conversationId, params) { + return request({ url: '/admin/session/' + conversationId + '/messages', method: 'get', params }) +} + // 删除会话 export function sessionDeleteApi(id) { return request({ url: '/admin/session/delete/' + id, method: 'post' }) } + +// 删除消息 +export function sessionMessageDeleteApi(messageId) { + return request({ url: '/admin/session/message/delete/' + messageId, method: 'post' }) +} + +// 撤回消息 +export function sessionMessageRecallApi(messageId) { + return request({ url: '/admin/session/message/recall/' + messageId, method: 'post' }) +} + +// 会话统计 +export function sessionStatisticsApi() { + return request({ url: '/admin/session/statistics', method: 'get' }) +} diff --git a/Zhibo/admin/src/views/session/list/index.vue b/Zhibo/admin/src/views/session/list/index.vue index cf03b96a..3e25b12e 100644 --- a/Zhibo/admin/src/views/session/list/index.vue +++ b/Zhibo/admin/src/views/session/list/index.vue @@ -1,164 +1,602 @@ - diff --git a/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/SessionController.java b/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/SessionController.java index 58701259..30a6eb7b 100644 --- a/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/SessionController.java +++ b/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/SessionController.java @@ -10,80 +10,302 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * 会话管理控制器(社交互动模块) + * 与私聊管理使用相同的数据表:eb_conversation 和 eb_private_message + */ @Slf4j @RestController @RequestMapping("api/admin/session") -@Api(tags = "会话管理") +@Api(tags = "社交互动 - 会话管理") @Validated public class SessionController { @Autowired private JdbcTemplate jdbcTemplate; + /** + * 获取会话列表 + */ @ApiOperation(value = "会话列表") - @RequestMapping(value = "/list", method = RequestMethod.GET) + @GetMapping("/list") public CommonResult>> getList( @RequestParam(value = "senderNickname", required = false) String senderNickname, @RequestParam(value = "senderPhone", required = false) String senderPhone, + @RequestParam(value = "keyword", required = false) String keyword, @RequestParam(value = "startTime", required = false) String startTime, @RequestParam(value = "endTime", required = false) String endTime, @RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "limit", defaultValue = "10") Integer limit) { - - StringBuilder sql = new StringBuilder("SELECT * FROM eb_session WHERE 1=1"); - StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_session WHERE 1=1"); - - if (senderNickname != null && !senderNickname.isEmpty()) { - String safeName = senderNickname.replace("'", "''"); - sql.append(" AND sender_nickname LIKE '%").append(safeName).append("%'"); - countSql.append(" AND sender_nickname LIKE '%").append(safeName).append("%'"); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT c.id, c.user1_id, c.user2_id, c.last_message, c.last_message_time, c.create_time, "); + sql.append("u1.nickname as sender_nickname, u1.avatar as sender_avatar, u1.phone as sender_phone, "); + sql.append("u2.nickname as receiver_nickname, u2.avatar as receiver_avatar, u2.phone as receiver_phone, "); + sql.append("(SELECT COUNT(*) FROM eb_private_message WHERE conversation_id = c.id) as message_count "); + sql.append("FROM eb_conversation c "); + sql.append("LEFT JOIN eb_user u1 ON c.user1_id = u1.uid "); + sql.append("LEFT JOIN eb_user u2 ON c.user2_id = u2.uid "); + sql.append("WHERE 1=1 "); + + StringBuilder countSql = new StringBuilder(); + countSql.append("SELECT COUNT(*) FROM eb_conversation c "); + countSql.append("LEFT JOIN eb_user u1 ON c.user1_id = u1.uid "); + countSql.append("LEFT JOIN eb_user u2 ON c.user2_id = u2.uid "); + countSql.append("WHERE 1=1 "); + + // 发送方昵称搜索 + if (senderNickname != null && !senderNickname.trim().isEmpty()) { + String condition = " AND (u1.nickname LIKE '%" + senderNickname.replace("'", "''") + "%' OR u2.nickname LIKE '%" + senderNickname.replace("'", "''") + "%') "; + sql.append(condition); + countSql.append(condition); } - if (senderPhone != null && !senderPhone.isEmpty()) { - String safePhone = senderPhone.replace("'", "''"); - sql.append(" AND sender_phone LIKE '%").append(safePhone).append("%'"); - countSql.append(" AND sender_phone LIKE '%").append(safePhone).append("%'"); + + // 发送方电话搜索 + if (senderPhone != null && !senderPhone.trim().isEmpty()) { + String condition = " AND (u1.phone LIKE '%" + senderPhone.replace("'", "''") + "%' OR u2.phone LIKE '%" + senderPhone.replace("'", "''") + "%') "; + sql.append(condition); + countSql.append(condition); } - if (startTime != null && !startTime.isEmpty()) { - sql.append(" AND create_time >= '").append(startTime).append("'"); - countSql.append(" AND create_time >= '").append(startTime).append("'"); + + // 关键词搜索(兼容私聊管理的参数) + if (keyword != null && !keyword.trim().isEmpty()) { + String condition = " AND (u1.nickname LIKE '%" + keyword + "%' OR u2.nickname LIKE '%" + keyword + "%' " + + "OR c.user1_id = '" + keyword + "' OR c.user2_id = '" + keyword + "') "; + sql.append(condition); + countSql.append(condition); } - if (endTime != null && !endTime.isEmpty()) { - sql.append(" AND create_time <= '").append(endTime).append("'"); - countSql.append(" AND create_time <= '").append(endTime).append("'"); + + // 时间范围 + if (startTime != null && !startTime.trim().isEmpty()) { + String condition = " AND c.create_time >= '" + startTime + "' "; + sql.append(condition); + countSql.append(condition); } - - sql.append(" ORDER BY id DESC"); - Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class); - + if (endTime != null && !endTime.trim().isEmpty()) { + String condition = " AND c.create_time <= '" + endTime + "' "; + sql.append(condition); + countSql.append(condition); + } + + // 排序 + sql.append("ORDER BY c.last_message_time DESC "); + + // 统计总数 + Long total = 0L; + try { + total = jdbcTemplate.queryForObject(countSql.toString(), Long.class); + } catch (Exception e) { + log.error("查询会话总数失败: {}", e.getMessage()); + } + + // 分页 int offset = (page - 1) * limit; - sql.append(" LIMIT ").append(offset).append(", ").append(limit); - List> list = jdbcTemplate.queryForList(sql.toString()); - + sql.append("LIMIT ").append(offset).append(", ").append(limit); + + List> list; + try { + list = jdbcTemplate.queryForList(sql.toString()); + } catch (Exception e) { + log.error("查询会话列表失败: {}", e.getMessage()); + list = java.util.Collections.emptyList(); + } + CommonPage> result = new CommonPage<>(); result.setList(list); - result.setTotal(total); + result.setTotal(total != null ? total : 0L); result.setPage(page); result.setLimit(limit); - result.setTotalPage((int) Math.ceil((double) total / limit)); - + result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit)); + return CommonResult.success(result); } + /** + * 获取会话详情 + */ @ApiOperation(value = "会话详情") - @RequestMapping(value = "/detail/{id}", method = RequestMethod.GET) - public CommonResult> getDetail(@PathVariable Integer id) { - String sql = "SELECT * FROM eb_session WHERE id = ?"; - Map detail = jdbcTemplate.queryForMap(sql, id); - return CommonResult.success(detail); + @GetMapping("/detail/{id}") + public CommonResult> getDetail(@PathVariable Long id) { + try { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT c.*, "); + sql.append("u1.nickname as sender_nickname, u1.avatar as sender_avatar, u1.phone as sender_phone, "); + sql.append("u2.nickname as receiver_nickname, u2.avatar as receiver_avatar, u2.phone as receiver_phone "); + sql.append("FROM eb_conversation c "); + sql.append("LEFT JOIN eb_user u1 ON c.user1_id = u1.uid "); + sql.append("LEFT JOIN eb_user u2 ON c.user2_id = u2.uid "); + sql.append("WHERE c.id = ?"); + + Map detail = jdbcTemplate.queryForMap(sql.toString(), id); + return CommonResult.success(detail); + } catch (Exception e) { + log.error("获取会话详情失败: {}", e.getMessage()); + return CommonResult.failed("获取会话详情失败"); + } } + /** + * 获取会话消息列表 + */ + @ApiOperation(value = "会话消息列表") + @GetMapping("/{conversationId}/messages") + public CommonResult>> getMessages( + @PathVariable Long conversationId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT m.*, u.nickname as sender_name, u.avatar as sender_avatar "); + sql.append("FROM eb_private_message m "); + sql.append("LEFT JOIN eb_user u ON m.sender_id = u.uid "); + sql.append("WHERE m.conversation_id = ? "); + sql.append("ORDER BY m.create_time DESC "); + + Long total = 0L; + try { + String countSql = "SELECT COUNT(*) FROM eb_private_message WHERE conversation_id = ?"; + total = jdbcTemplate.queryForObject(countSql, Long.class, conversationId); + } catch (Exception e) { + log.error("查询消息总数失败: {}", e.getMessage()); + } + + int offset = (page - 1) * pageSize; + sql.append("LIMIT ").append(offset).append(", ").append(pageSize); + + List> list; + try { + list = jdbcTemplate.queryForList(sql.toString(), conversationId); + // 转换字段名 + list.forEach(item -> { + item.put("senderId", item.get("sender_id")); + item.put("receiverId", item.get("receiver_id")); + item.put("senderName", item.get("sender_name")); + item.put("senderAvatar", item.get("sender_avatar")); + item.put("messageType", item.get("message_type")); + item.put("createTime", item.get("create_time")); + item.put("isRecalled", item.get("is_recalled")); + item.put("originalContent", item.get("original_content")); + item.put("recallTime", item.get("recall_time")); + }); + } catch (Exception e) { + log.error("查询消息列表失败: {}", e.getMessage()); + list = java.util.Collections.emptyList(); + } + + CommonPage> result = new CommonPage<>(); + result.setList(list); + result.setTotal(total != null ? total : 0L); + result.setPage(page); + result.setLimit(pageSize); + result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / pageSize)); + + return CommonResult.success(result); + } + + /** + * 删除会话(包括所有消息) + */ @ApiOperation(value = "删除会话") - @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST) - public CommonResult delete(@PathVariable Integer id) { - jdbcTemplate.update("DELETE FROM eb_session WHERE id = ?", id); - return CommonResult.success("删除成功"); + @PostMapping("/delete/{id}") + public CommonResult delete(@PathVariable Long id) { + try { + // 先删除消息 + jdbcTemplate.update("DELETE FROM eb_private_message WHERE conversation_id = ?", id); + // 删除会话 + jdbcTemplate.update("DELETE FROM eb_conversation WHERE id = ?", id); + return CommonResult.success("删除成功"); + } catch (Exception e) { + log.error("删除会话失败: {}", e.getMessage()); + return CommonResult.failed("删除失败: " + e.getMessage()); + } + } + + /** + * 删除单条消息 + */ + @ApiOperation(value = "删除消息") + @PostMapping("/message/delete/{messageId}") + public CommonResult deleteMessage(@PathVariable Long messageId) { + try { + jdbcTemplate.update("DELETE FROM eb_private_message WHERE id = ?", messageId); + return CommonResult.success("删除成功"); + } catch (Exception e) { + log.error("删除消息失败: {}", e.getMessage()); + return CommonResult.failed("删除失败: " + e.getMessage()); + } + } + + /** + * 撤回消息(管理员可以撤回任意消息) + */ + @ApiOperation(value = "撤回消息") + @PostMapping("/message/recall/{messageId}") + public CommonResult recallMessage(@PathVariable Long messageId) { + try { + // 先获取原始消息内容 + Map message = jdbcTemplate.queryForMap( + "SELECT content FROM eb_private_message WHERE id = ?", messageId); + String originalContent = (String) message.get("content"); + + // 保存原始内容并标记为撤回 + int updated = jdbcTemplate.update( + "UPDATE eb_private_message SET is_recalled = 1, original_content = ?, recall_time = NOW(), content = '[消息已被管理员撤回]' WHERE id = ?", + originalContent, messageId + ); + if (updated > 0) { + return CommonResult.success("撤回成功"); + } else { + return CommonResult.failed("消息不存在"); + } + } catch (Exception e) { + log.error("撤回消息失败: {}", e.getMessage()); + return CommonResult.failed("撤回失败: " + e.getMessage()); + } + } + + /** + * 获取会话统计数据 + */ + @ApiOperation(value = "会话统计") + @GetMapping("/statistics") + public CommonResult> getStatistics() { + Map stats = new HashMap<>(); + + try { + // 总会话数 + Long totalConversations = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM eb_conversation", Long.class); + stats.put("totalConversations", totalConversations != null ? totalConversations : 0); + + // 总消息数 + Long totalMessages = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM eb_private_message", Long.class); + stats.put("totalMessages", totalMessages != null ? totalMessages : 0); + + // 今日消息数 + Long todayMessages = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM eb_private_message WHERE DATE(create_time) = CURDATE()", Long.class); + stats.put("todayMessages", todayMessages != null ? todayMessages : 0); + + // 活跃会话数(最近7天有消息的会话) + Long activeConversations = jdbcTemplate.queryForObject( + "SELECT COUNT(DISTINCT conversation_id) FROM eb_private_message WHERE create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)", + Long.class); + stats.put("activeConversations", activeConversations != null ? activeConversations : 0); + + } catch (Exception e) { + log.error("获取统计数据失败: {}", e.getMessage()); + stats.put("totalConversations", 0); + stats.put("totalMessages", 0); + stats.put("todayMessages", 0); + stats.put("activeConversations", 0); + } + + return CommonResult.success(stats); } } diff --git a/Zhibo/zhibo-h/crmeb-common/src/main/java/com/zbkj/common/model/chat/PrivateMessage.java b/Zhibo/zhibo-h/crmeb-common/src/main/java/com/zbkj/common/model/chat/PrivateMessage.java index 1d65fab9..a1ca6b17 100644 --- a/Zhibo/zhibo-h/crmeb-common/src/main/java/com/zbkj/common/model/chat/PrivateMessage.java +++ b/Zhibo/zhibo-h/crmeb-common/src/main/java/com/zbkj/common/model/chat/PrivateMessage.java @@ -82,4 +82,12 @@ public class PrivateMessage implements Serializable { @ApiModelProperty(value = "是否已撤回") @Column(name = "is_recalled", columnDefinition = "TINYINT(1) DEFAULT 0") private Boolean isRecalled; + + @ApiModelProperty(value = "原始消息内容(撤回前)") + @Column(name = "original_content", columnDefinition = "TEXT") + private String originalContent; + + @ApiModelProperty(value = "撤回时间") + @Column(name = "recall_time") + private Date recallTime; } diff --git a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/CallController.java b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/CallController.java index 21da4512..41134a81 100644 --- a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/CallController.java +++ b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/CallController.java @@ -229,3 +229,4 @@ public class CallController { return response; } } + \ No newline at end of file diff --git a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/ConversationController.java b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/ConversationController.java index ec6ce6b0..b2d42df6 100644 --- a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/ConversationController.java +++ b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/ConversationController.java @@ -145,4 +145,15 @@ public class ConversationController { Integer userId = userService.getUserIdException(); return CommonResult.success(conversationService.deleteMessage(id, userId)); } + + /** + * 撤回消息 + */ + @ApiOperation(value = "撤回消息") + @ApiImplicitParam(name = "id", value = "消息ID", required = true) + @PostMapping("/messages/{id}/recall") + public CommonResult recallMessage(@PathVariable Long id) { + Integer userId = userService.getUserIdException(); + return CommonResult.success(conversationService.recallMessage(id, userId)); + } } diff --git a/Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/service/impl/ConversationServiceImpl.java b/Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/service/impl/ConversationServiceImpl.java index f4be294c..cab375ac 100644 --- a/Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/service/impl/ConversationServiceImpl.java +++ b/Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/service/impl/ConversationServiceImpl.java @@ -412,10 +412,12 @@ public class ConversationServiceImpl extends ServiceImpl 2) { throw new CrmebException("消息发送超过2分钟,无法撤回"); } - // 标记消息为已撤回 + // 保存原始内容,然后标记消息为已撤回 LambdaUpdateWrapper uw = new LambdaUpdateWrapper<>(); uw.eq(PrivateMessage::getId, messageId) .set(PrivateMessage::getIsRecalled, true) + .set(PrivateMessage::getOriginalContent, message.getContent()) // 保存原始内容 + .set(PrivateMessage::getRecallTime, new Date()) // 记录撤回时间 .set(PrivateMessage::getContent, "[消息已撤回]"); return privateMessageDao.update(null, uw) > 0; } diff --git a/Zhibo/zhibo-h/sql/add_recall_fields.sql b/Zhibo/zhibo-h/sql/add_recall_fields.sql new file mode 100644 index 00000000..4ccfef73 --- /dev/null +++ b/Zhibo/zhibo-h/sql/add_recall_fields.sql @@ -0,0 +1,8 @@ +-- 为私信消息表添加撤回相关字段 +-- 执行此脚本前请备份数据库 + +-- 添加原始内容字段(保存撤回前的消息内容) +ALTER TABLE eb_private_message ADD COLUMN original_content TEXT COMMENT '原始消息内容(撤回前)'; + +-- 添加撤回时间字段 +ALTER TABLE eb_private_message ADD COLUMN recall_time DATETIME COMMENT '撤回时间'; diff --git a/android-app/app/src/main/java/com/example/livestreaming/ConversationActivity.java b/android-app/app/src/main/java/com/example/livestreaming/ConversationActivity.java index d2712b55..ffc8b50c 100644 --- a/android-app/app/src/main/java/com/example/livestreaming/ConversationActivity.java +++ b/android-app/app/src/main/java/com/example/livestreaming/ConversationActivity.java @@ -109,6 +109,9 @@ public class ConversationActivity extends AppCompatActivity { setupMessages(); setupInput(); + // 确保输入框显示正确的提示文本 + binding.messageInput.setHint("输入消息..."); + // 标记会话为已读 if (initialUnreadCount > 0 && conversationId != null) { markConversationAsRead(); @@ -414,9 +417,16 @@ public class ConversationActivity extends AppCompatActivity { PopupMenu popupMenu = new PopupMenu(this, anchorView); popupMenu.getMenu().add(0, 0, 0, "复制"); popupMenu.getMenu().add(0, 2, 0, "表情回应"); - // 只有自己发送的消息才能删除 - if ("我".equals(message.getUsername())) { + // 只有自己发送的消息才能删除和撤回 + if ("我".equals(message.getUsername()) || message.isOutgoing()) { popupMenu.getMenu().add(0, 1, 0, "删除"); + // 检查是否在2分钟内,可以撤回 + long messageTime = message.getTimestamp(); + long now = System.currentTimeMillis(); + long diffMinutes = (now - messageTime) / (1000 * 60); + if (diffMinutes <= 2) { + popupMenu.getMenu().add(0, 3, 0, "撤回"); + } } popupMenu.setOnMenuItemClickListener(item -> { @@ -429,6 +439,9 @@ public class ConversationActivity extends AppCompatActivity { } else if (item.getItemId() == 2) { showEmojiPicker(message); return true; + } else if (item.getItemId() == 3) { + recallMessage(message, position); + return true; } return false; }); @@ -497,6 +510,68 @@ public class ConversationActivity extends AppCompatActivity { }); } + /** + * 撤回消息 + */ + private void recallMessage(ChatMessage message, int position) { + if (position < 0 || position >= messages.size()) return; + + new AlertDialog.Builder(this) + .setTitle("撤回消息") + .setMessage("确定要撤回这条消息吗?") + .setPositiveButton("撤回", (dialog, which) -> recallMessageFromServer(message, position)) + .setNegativeButton("取消", null) + .show(); + } + + /** + * 调用服务器撤回消息接口 + */ + private void recallMessageFromServer(ChatMessage message, int position) { + String token = AuthStore.getToken(this); + if (token == null) return; + + String messageId = message.getMessageId(); + String url = ApiConfig.getBaseUrl() + "/api/front/conversations/messages/" + messageId + "/recall"; + Log.d(TAG, "撤回消息: " + url); + + Request request = new Request.Builder() + .url(url) + .addHeader("Authori-zation", token) + .post(RequestBody.create("", MediaType.parse("application/json"))) + .build(); + + httpClient.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + Log.e(TAG, "撤回消息失败", e); + runOnUiThread(() -> Snackbar.make(binding.getRoot(), "撤回失败", Snackbar.LENGTH_SHORT).show()); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + String body = response.body() != null ? response.body().string() : ""; + Log.d(TAG, "撤回消息响应: " + body); + runOnUiThread(() -> { + try { + JSONObject json = new JSONObject(body); + if (json.optInt("code", -1) == 200) { + // 更新本地消息显示为已撤回 + message.setMessage("[消息已撤回]"); + adapter.notifyItemChanged(position); + Snackbar.make(binding.getRoot(), "消息已撤回", Snackbar.LENGTH_SHORT).show(); + } else { + String errorMsg = json.optString("message", "撤回失败"); + Snackbar.make(binding.getRoot(), errorMsg, Snackbar.LENGTH_SHORT).show(); + } + } catch (Exception e) { + Log.e(TAG, "解析撤回响应失败", e); + } + }); + } + }); + } + private void setupInput() { binding.sendButton.setOnClickListener(new DebounceClickListener(300) { diff --git a/android-app/app/src/main/res/layout/activity_conversation.xml b/android-app/app/src/main/res/layout/activity_conversation.xml index b0e5cf61..7b5fb33d 100644 --- a/android-app/app/src/main/res/layout/activity_conversation.xml +++ b/android-app/app/src/main/res/layout/activity_conversation.xml @@ -123,7 +123,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"> - + android:textColorHint="#999999" + android:textSize="14sp" + android:importantForAutofill="no" + android:autofillHints="" />