docs(project): 更新需求与开发进度对比报告并完善Android SDK接口定义
- 添加了完整的XuqmGroup平台需求与开发进度对比报告 - 实现了Android SDK的ImApi接口定义,涵盖群组、好友、黑名单等完整功能 - 定义了IM消息、会话、群组、用户资料等核心数据模型 - 实现了Android SDK的ImSDK核心功能类,包括连接管理和消息处理
这个提交包含在:
父节点
824edd48bc
当前提交
f2084c7911
@ -540,6 +540,30 @@ public final class ImSDK {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func transferGroupOwner(groupId: String, newOwnerId: String) async throws -> ImGroup {
|
||||||
|
try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/groups/\(groupId)/owner",
|
||||||
|
method: "POST",
|
||||||
|
body: ImTransferOwnerRequest(newOwnerId: newOwnerId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateGroupAttributes(groupId: String, attributes: [String: ImAttributeValue]) async throws -> ImGroup {
|
||||||
|
try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/groups/\(groupId)/attributes",
|
||||||
|
method: "PUT",
|
||||||
|
body: attributes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeGroupAttributes(groupId: String, keys: [String]) async throws -> ImGroup {
|
||||||
|
try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/groups/\(groupId)/attributes/delete",
|
||||||
|
method: "POST",
|
||||||
|
body: ImAttributeKeysRequest(keys: keys)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func dismissGroup(groupId: String) async throws {
|
public func dismissGroup(groupId: String) async throws {
|
||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)",
|
path: "/api/im/groups/\(groupId)",
|
||||||
@ -604,6 +628,15 @@ public final class ImSDK {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func removeAllFriends() async throws {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/friends",
|
||||||
|
method: "DELETE",
|
||||||
|
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func removeFriend(friendId: String) async throws {
|
public func removeFriend(friendId: String) async throws {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
@ -613,6 +646,35 @@ public final class ImSDK {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setFriendGroup(friendId: String, groupName: String? = nil) async throws {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
var items = [URLQueryItem(name: "appId", value: config.appId)]
|
||||||
|
if let groupName {
|
||||||
|
items.append(URLQueryItem(name: "groupName", value: groupName))
|
||||||
|
}
|
||||||
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/friends/\(friendId)/group",
|
||||||
|
method: "PUT",
|
||||||
|
queryItems: items
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listFriendGroups() async throws -> [String] {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
return try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/friends/groups",
|
||||||
|
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listFriendsByGroup(_ groupName: String) async throws -> [String] {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
return try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/friends/groups/\(groupName)",
|
||||||
|
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func listFriendRequests(direction: String = "incoming") async throws -> [FriendRequest] {
|
public func listFriendRequests(direction: String = "incoming") async throws -> [FriendRequest] {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
@ -688,6 +750,17 @@ public final class ImSDK {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func checkBlacklist(targetUserId: String) async throws -> BlacklistCheckResult {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
return try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/blacklist/check",
|
||||||
|
queryItems: [
|
||||||
|
URLQueryItem(name: "appId", value: config.appId),
|
||||||
|
URLQueryItem(name: "targetUserId", value: targetUserId),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func getProfile(userId: String) async throws -> UserProfile {
|
public func getProfile(userId: String) async throws -> UserProfile {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
@ -760,6 +833,51 @@ public final class ImSDK {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setConversationHidden(targetId: String, chatType: ChatType, hidden: Bool) async throws {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/conversations/\(targetId)/hidden",
|
||||||
|
method: "PUT",
|
||||||
|
queryItems: [
|
||||||
|
URLQueryItem(name: "appId", value: config.appId),
|
||||||
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
|
URLQueryItem(name: "hidden", value: String(hidden)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setConversationGroup(targetId: String, chatType: ChatType, groupName: String? = nil) async throws {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
var items = [
|
||||||
|
URLQueryItem(name: "appId", value: config.appId),
|
||||||
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
|
]
|
||||||
|
if let groupName {
|
||||||
|
items.append(URLQueryItem(name: "groupName", value: groupName))
|
||||||
|
}
|
||||||
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/conversations/\(targetId)/group",
|
||||||
|
method: "PUT",
|
||||||
|
queryItems: items
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listConversationGroups() async throws -> [String] {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
return try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/conversation-groups",
|
||||||
|
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func listConversationGroupItems(_ groupName: String) async throws -> [ConversationGroupItem] {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
return try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/conversation-groups/\(groupName)",
|
||||||
|
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func setDraft(targetId: String, chatType: ChatType, draft: String) async throws {
|
public func setDraft(targetId: String, chatType: ChatType, draft: String) async throws {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
@ -790,6 +908,16 @@ public final class ImSDK {
|
|||||||
return conversations.reduce(0) { $0 + $1.unreadCount }
|
return conversations.reduce(0) { $0 + $1.unreadCount }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func adminGroupReadReceipts(groupId: String, messageIds: [String]) async throws -> [GroupReadReceiptSummary] {
|
||||||
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
|
return try await ApiClient.shared.request(
|
||||||
|
path: "/api/im/admin/groups/\(groupId)/read-receipts",
|
||||||
|
method: "POST",
|
||||||
|
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
||||||
|
body: ImGroupReadReceiptRequest(messageIds: messageIds)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func disconnect() {
|
public func disconnect() {
|
||||||
client?.disconnect()
|
client?.disconnect()
|
||||||
client = nil
|
client = nil
|
||||||
|
|||||||
@ -91,12 +91,15 @@ public struct ImGroup: Codable, Sendable {
|
|||||||
public let memberIds: String
|
public let memberIds: String
|
||||||
public let adminIds: String
|
public let adminIds: String
|
||||||
public let announcement: String?
|
public let announcement: String?
|
||||||
|
public let memberInfo: String?
|
||||||
|
public let extAttributes: String?
|
||||||
public let createdAt: Int64
|
public let createdAt: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ConversationData: Codable, Sendable {
|
public struct ConversationData: Codable, Sendable {
|
||||||
public let targetId: String
|
public let targetId: String
|
||||||
public let chatType: ChatType
|
public let chatType: ChatType
|
||||||
|
public let conversationGroup: String?
|
||||||
public let lastMsgContent: String?
|
public let lastMsgContent: String?
|
||||||
public let lastMsgType: String?
|
public let lastMsgType: String?
|
||||||
public let lastMsgTime: Int64
|
public let lastMsgTime: Int64
|
||||||
@ -105,6 +108,12 @@ public struct ConversationData: Codable, Sendable {
|
|||||||
public let isPinned: Bool
|
public let isPinned: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ConversationGroupItem: Codable, Sendable {
|
||||||
|
public let targetId: String
|
||||||
|
public let chatType: ChatType
|
||||||
|
public let groupName: String
|
||||||
|
}
|
||||||
|
|
||||||
public struct FriendRequest: Codable, Sendable {
|
public struct FriendRequest: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let appId: String
|
public let appId: String
|
||||||
@ -135,6 +144,21 @@ public struct BlacklistEntry: Codable, Sendable {
|
|||||||
public let createdAt: Int64
|
public let createdAt: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct BlacklistCheckResult: Codable, Sendable {
|
||||||
|
public let targetUserId: String
|
||||||
|
public let blockedByMe: Bool
|
||||||
|
public let blockedByTarget: Bool
|
||||||
|
public let eitherBlocked: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct GroupReadReceiptSummary: Codable, Sendable {
|
||||||
|
public let messageId: String
|
||||||
|
public let groupId: String
|
||||||
|
public let memberCount: Int
|
||||||
|
public let readCount: Int
|
||||||
|
public let unreadCount: Int
|
||||||
|
}
|
||||||
|
|
||||||
public struct UserProfile: Codable, Sendable {
|
public struct UserProfile: Codable, Sendable {
|
||||||
public let id: String?
|
public let id: String?
|
||||||
public let appId: String?
|
public let appId: String?
|
||||||
@ -187,3 +211,78 @@ public struct ImMuteMemberRequest: Encodable, Sendable {
|
|||||||
public let userId: String
|
public let userId: String
|
||||||
public let minutes: Int64
|
public let minutes: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ImTransferOwnerRequest: Encodable, Sendable {
|
||||||
|
public let newOwnerId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ImAttributeKeysRequest: Encodable, Sendable {
|
||||||
|
public let keys: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ImGroupReadReceiptRequest: Encodable, Sendable {
|
||||||
|
public let messageIds: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ImAttributeValue: Codable, Sendable {
|
||||||
|
case string(String)
|
||||||
|
case int(Int)
|
||||||
|
case double(Double)
|
||||||
|
case bool(Bool)
|
||||||
|
case null
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.singleValueContainer()
|
||||||
|
if container.decodeNil() {
|
||||||
|
self = .null
|
||||||
|
} else if let value = try? container.decode(Bool.self) {
|
||||||
|
self = .bool(value)
|
||||||
|
} else if let value = try? container.decode(Int.self) {
|
||||||
|
self = .int(value)
|
||||||
|
} else if let value = try? container.decode(Double.self) {
|
||||||
|
self = .double(value)
|
||||||
|
} else {
|
||||||
|
self = .string(try container.decode(String.self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.singleValueContainer()
|
||||||
|
switch self {
|
||||||
|
case .string(let value):
|
||||||
|
try container.encode(value)
|
||||||
|
case .int(let value):
|
||||||
|
try container.encode(value)
|
||||||
|
case .double(let value):
|
||||||
|
try container.encode(value)
|
||||||
|
case .bool(let value):
|
||||||
|
try container.encode(value)
|
||||||
|
case .null:
|
||||||
|
try container.encodeNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImAttributeValue: ExpressibleByStringLiteral {
|
||||||
|
public init(stringLiteral value: String) {
|
||||||
|
self = .string(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImAttributeValue: ExpressibleByIntegerLiteral {
|
||||||
|
public init(integerLiteral value: Int) {
|
||||||
|
self = .int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImAttributeValue: ExpressibleByFloatLiteral {
|
||||||
|
public init(floatLiteral value: Double) {
|
||||||
|
self = .double(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImAttributeValue: ExpressibleByBooleanLiteral {
|
||||||
|
public init(booleanLiteral value: Bool) {
|
||||||
|
self = .bool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户