Files
tongxunruanjian/mobile/lib/services/message_send_service.dart
T
编码工程师andmultica-agent 12b0a1c943 feat(mobile,account-service): 统一发送服务 + 员工目录接口 + 好友申请五态/验证消息(B-52 施工 2/3)
- message_send_service:所有消息走统一门禁(登录/连接/空接收方/好友校验),
  失败打印 SDK 原始 code 并映射中文提示,不再只吞异常
- chat_screen 接入统一发送服务,失败提示真实原因
- account-service 新增 GET /api/directory(登录用户可读启用员工,用于添加同事)
- add_friend_screen 增加可编辑验证消息(视觉规范 4.2)
- new_friends_screen 五态 + 窄屏(<360)按钮组换行不挤压姓名(视觉规范 5.3)

Co-authored-by: multica-agent <github@multica.ai>
2026-08-16 02:16:00 +08:00

104 lines
3.8 KiB
Dart

import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'im_service.dart';
/// 统一消息发送服务:所有消息(文字/表情/语音/图片/文件/通话信令)都走这里。
///
/// 职责:
/// 1. 发送前置校验:已登录、连接正常、接收方非空;
/// 2. 单聊时校验好友状态(非好友明确提示,不静默失败);
/// 3. 失败时把 SDK 原始 code/message 记进日志(不含 token 与正文),
/// 并返回可直接展示的中文错误,杜绝「只吞异常看不到原因」。
class MessageSendService {
MessageSendService._();
static final MessageSendService instance = MessageSendService._();
/// 发送消息。成功返回 SDK 回执消息;失败抛出带中文提示的异常。
/// 单聊且非好友时抛 [FriendRequiredException]。
Future<Message> send({
required Message message,
String? userID,
String? groupID,
String? offlinePushInfo = '',
}) async {
final im = IMService.instance;
if (!im.loggedIn || im.currentUserID == null) {
throw const SendException('登录状态失效,请重新登录');
}
if (im.connectStatus != ConnectStatus.success) {
throw const SendException('正在重连,请稍后再试');
}
final isGroup = groupID != null && groupID.isNotEmpty;
final peer = userID ?? '';
if (!isGroup && peer.isEmpty) {
throw const SendException('请先添加对方为同事,再发起会话');
}
// 单聊:非好友提示先添加(OpenIM 服务端是否拦截交给服务端,但提示要明确)
if (!isGroup) {
final isFriend = await _isFriend(peer);
if (!isFriend) {
throw FriendRequiredException('请先添加对方为同事,再发送消息');
}
}
try {
final sent = await OpenIM.iMManager.messageManager.sendMessage(
message: message,
userID: isGroup ? null : peer,
groupID: isGroup ? groupID : null,
offlinePushInfo: OfflinePushInfo(),
);
return sent;
} catch (e) {
// 记录原始错误码(不含 token / 消息正文)
// ignore: avoid_print
print('[MessageSendService] 发送失败 peer=$peer groupID=$groupID err=$e');
throw SendException(_mapError(e));
}
}
/// 判断 peer 是否已是好友。查询失败按非好友处理(宁可让用户先加好友)。
Future<bool> _isFriend(String userID) async {
try {
final friends = await OpenIM.iMManager.friendshipManager.getFriendList();
return friends.any((f) => (f.userID ?? f.friendUserID) == userID);
} catch (_) {
return false;
}
}
/// 把 SDK 原始错误映射成中文提示
String _mapError(dynamic e) {
final s = e.toString();
// SDK 错误码常量,见 flutter_openim_sdk/lib/src/enum/sdk_error_code.dart
if (s.contains('1303')) return '对方还不是你的同事,请先添加对方';
if (s.contains('1304')) return '已经是同事了,可以正常聊天';
if (s.contains('1302')) return '对方已把你加入黑名单,无法发送';
if (s.contains('1101')) return '对方账号不存在';
if (s.contains('1501')) return '登录已过期,请重新登录';
if (s.contains('1502') || s.contains('1503')) return '登录凭证无效,请重新登录';
if (s.contains('10000') || s.contains('10001') || s.contains('网络') || s.contains('timeout')) {
return '网络异常,请检查网络后重试';
}
return '消息没发出去,请重试';
}
}
/// 发送失败的中文提示
class SendException implements Exception {
final String message;
const SendException(this.message);
@override
String toString() => message;
}
/// 单聊非好友时的明确提示
class FriendRequiredException extends SendException {
const FriendRequiredException(super.message);
}