jsdw_ios/QuickLocation/Model/GroupModel.swift

446 lines
12 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// GroupModel.swift
// QuickLocation
//
// Created by on 2026/5/30.
//
import ObjectMapper
import RxDataSources
import Kingfisher
private let kGroupTransformInt64 = TransformOf<Int64, Any>(fromJSON: { value in
switch value {
case let value as Int64:
return value
case let value as Int:
return Int64(value)
case let value as Double:
return Int64(value)
case let value as String:
return Int64(value)
default:
return nil
}
}, toJSON: { value in
value
})
///
struct UserGroupResponse: BaseModelProtocol {
//
var code: String?
//
var message: String?
//
var model: GroupModel?
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- map["code"]
message <- map["msg"]
model <- map["data"]
}
}
///
struct RecommandGroupResponse: BaseModelProtocol {
//
var code: String?
//
var message: String?
//
var list: [GroupInfoModel] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- map["code"]
message <- map["msg"]
list <- map["data"]
}
}
///
struct GroupInfoResponse: BaseModelProtocol {
//
var code: String?
//
var message: String?
//
var model: GroupInfoModel?
//
var list: [GroupMemberModel] = []
//
var reviewCount: Int = 0
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- map["code"]
message <- map["msg"]
model <- map["data.attr"]
list <- map["data.employee"]
reviewCount <- map["data.reviewCount"]
}
}
///
struct GroupUsersResponse: BaseModelProtocol {
var code: String?
var message: String?
var model: GroupInfoModel?
var list: [GroupMemberModel] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- (map["code"], kIntTransformStr)
message <- map["message"]
model <- map["data.group"]
list <- map["data.mates"]
}
func isValid(for groupKey: String) -> Bool {
code == "0" && model?.group_key == groupKey
}
}
///
struct GroupTemplateResponse: BaseModelProtocol {
var code: String?
var message: String?
var list: [GroupTemplateModel] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- (map["code"], kIntTransformStr)
message <- map["message"]
list <- map["data"]
}
}
struct GroupTemplateModel: Mappable, Equatable {
var name: String = ""
var type: Int = 0
var limit: Int = -1
var icon: String = ""
init?(map: Map) {}
mutating func mapping(map: Map) {
name <- map["name"]
type <- (map["type"], kStrTransformInt)
limit <- (map["limit"], kStrTransformInt)
icon <- map["icon"]
}
}
struct ReviewMemberListResponse: BaseModelProtocol {
//
var code: String?
//
var message: String?
///
var list: [GroupMemberModel] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- map["code"]
message <- map["msg"]
list <- map["data"]
}
}
struct GroupModel: Mappable {
///
var default_group_key: String = ""
///
var has_unread_email: Bool = false
/// SOS
var is_anyone_sos: Bool = false
///
var groups: [GroupInfoModel] = []
///
var select_group_employee: [GroupMemberModel] = []
var score: Int = 0
init?(map: Map) {
}
mutating func mapping(map: Map) {
default_group_key <- map["default_group_key"]
has_unread_email <- map["has_unread_email"]
is_anyone_sos <- map["is_anyone_sos"]
score <- map["score"]
groups <- map["groups"]
select_group_employee <- map["select_group_employee"]
}
}
struct GroupInfoModel: Mappable, Equatable {
///
var group_key: String = ""
///
var is_owner: Bool = false
///
var name: String = ""
/// icon
var icon_index: Int = 1
var groupIcon: UIImage {
UIImage(named: "GroupIcon/\(icon_index)") ?? UIImage()
}
/// icon_index
var group_type: Int = 0
var group_template: GroupTemplateModel?
var resolvedGroupType: Int {
if group_type > 0 { return group_type }
if let type = group_template?.type, type > 0 { return type }
return icon_index
}
var groupTemplateName: String {
let name = group_template?.name.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return name.isEmpty ? "未知类型" : name
}
var groupIconURL: String {
group_template?.icon.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
}
///
var labels: [String] = []
///
var review: Bool = false
///
var people_no: Int = 0
///
var description: String = ""
///
var level: String = ""
///
var share_code: String = ""
init?(map: Map) {
}
mutating func mapping(map: Map) {
group_key <- map["group_key"]
is_owner <- map["is_owner"]
name <- map["name"]
icon_index <- map["icon_index"]
group_type <- (map["group_type"], kStrTransformInt)
group_template <- map["group_template"]
labels <- map["labels"]
level <- (map["level"], kIntTransformStr)
review <- map["review"]
description <- map["description"]
share_code <- map["share_code"]
people_no <- (map["people_no"], kStrTransformInt)
}
}
extension UIImageView {
@MainActor
func setGroupIcon(_ model: GroupInfoModel) {
setGroupIcon(url: model.groupIconURL, fallback: model.groupIcon)
}
@MainActor
func setGroupIcon(url: String, fallback: UIImage?) {
kf.cancelDownloadTask()
image = fallback
guard let imageURL = URL(string: url), !url.isEmpty else { return }
kf.setImage(with: imageURL, placeholder: fallback) { [weak self] result in
if case .failure = result {
self?.image = fallback
}
}
}
}
extension GroupInfoModel: IdentifiableType {
public typealias Identity = String
public var identity: String {
return group_key
}
}
struct CommonGroupModel: Mappable, Equatable {
var group_key: String = ""
var group_name: String = ""
init(groupKey: String, groupName: String) {
group_key = groupKey
group_name = groupName
}
init?(map: Map) {}
mutating func mapping(map: Map) {
group_key <- map["group_key"]
group_name <- map["group_name"]
}
static func deduplicated(_ groups: [CommonGroupModel]) -> [CommonGroupModel] {
var seenKeys = Set<String>()
var result: [CommonGroupModel] = []
for group in groups {
let key = group.group_key.trimmingCharacters(in: .whitespacesAndNewlines)
guard !key.isEmpty, seenKeys.insert(key).inserted else { continue }
result.append(group)
}
return result
}
}
struct GroupMemberModel: Mappable, Equatable {
///
var user_id: String = ""
///
var head_pic: String = "1"
///
var userIcon: UIImage {
let iconName = head_pic.trimmingCharacters(in: .whitespacesAndNewlines)
guard !iconName.isEmpty,
let image = UIImage(named: "UserIcon/\(iconName)") else {
return UIImage(named: "UserIcon/1") ?? UIImage()
}
return image
}
///
var nick_name: String = ""
/// 1 2 3 4
var level: Int = 1
var vipIcon: UIImage {
switch level {
case 2: //
return UIImage(named: "VipIcon/2") ?? UIImage()
case 3: //
return UIImage(named: "VipIcon/1") ?? UIImage()
default:
return UIImage()
}
}
/// 0 1SOS 2
var status: Int = 0
///
var join_days: Int = 0
/// 使使
var join_time: Int64 = 0
/// 线
var is_online: Bool = false
///
var battery: String = ""
/// 0 1-36 MoodStatusCatalog
var mood: Int = 0
/// 线
var last_active_time: Int64 = 0
/// 线MQTT
var lastUpdateText: String {
let nowMs = Date().timeIntervalSince1970
let msgTimeMs = Double(last_active_time)
let diffSec = nowMs - msgTimeMs
let isOnline = diffSec < 60
let lastUpdateText: String
if isOnline {
lastUpdateText = ""
} else if diffSec < 3600 {
lastUpdateText = "\(Int(diffSec / 60))分钟前"
} else if diffSec < 86400 {
lastUpdateText = "\(Int(diffSec / 3600))小时前"
} else {
lastUpdateText = "\(Int(diffSec / 86400))天前"
}
return lastUpdateText
}
///
var last_position: String = ""
///
var common_groups: [CommonGroupModel] = []
var lastLocation: String {
guard last_position.contains(":") else { return "" }
let arr = last_position.components(separatedBy: ":")
return arr.last ?? ""
}
init?(map: Map) {
}
mutating func mapping(map: Map) {
user_id <- map["user_id"]
head_pic <- map["head_pic"]
level <- (map["level"], kStrTransformInt)
nick_name <- map["nick_name"]
battery <- map["battery"]
last_position <- map["last_position"]
is_online <- map["is_online"]
last_active_time <- map["last_active_time"]
status <- map["status"]
mood <- (map["mood"], kStrTransformInt)
join_days <- (map["join_days"], kStrTransformInt)
join_time <- (map["join_time"], kGroupTransformInt64)
common_groups <- map["common_groups"]
common_groups = CommonGroupModel.deduplicated(common_groups)
}
}
extension GroupMemberModel {
/// New responses use seconds; older payloads may still contain milliseconds.
var joinDate: Date? {
guard join_time > 0 else { return nil }
let seconds = join_time >= 100_000_000_000
? TimeInterval(join_time) / 1000
: TimeInterval(join_time)
return Date(timeIntervalSince1970: seconds)
}
}
enum GroupOwnerMatcher {
static func matches(userId: String, groupKey: String) -> Bool {
!userId.isEmpty && !groupKey.isEmpty && groupKey.contains(userId)
}
}
enum GroupMemberSorter {
/// -> -> 线 ->
static func sorted(_ members: [GroupMemberModel],
groupKey: String,
currentUserId: String) -> [GroupMemberModel] {
members.enumerated()
.sorted { lhs, rhs in
let lhsPriority = priority(lhs.element,
groupKey: groupKey,
currentUserId: currentUserId)
let rhsPriority = priority(rhs.element,
groupKey: groupKey,
currentUserId: currentUserId)
if lhsPriority != rhsPriority { return lhsPriority < rhsPriority }
return lhs.offset < rhs.offset
}
.map(\.element)
}
private static func priority(_ member: GroupMemberModel,
groupKey: String,
currentUserId: String) -> Int {
if GroupOwnerMatcher.matches(userId: member.user_id, groupKey: groupKey) { return 0 }
if !currentUserId.isEmpty, member.user_id == currentUserId { return 1 }
if member.is_online { return 2 }
return 3
}
}
extension GroupMemberModel: IdentifiableType {
public typealias Identity = String
public var identity: String {
return user_id
}
}