// // O2AuthSDK.swift // O2OA_SDK_Framwork // // Created by FancyLou on 2018/11/6. // import UIKit import RxSwift import Moya import CommonCrypto public class O2AuthSDK: NSObject { /// 单例 public static let shared: O2AuthSDK = O2AuthSDK() private override init() {} private let registerAPI: O2MoyaProvider = { return O2MoyaProvider() }() private let loginAPI: O2MoyaProvider = { return O2MoyaProvider() }() //MARK: - public 公开本地对象 /// 当前登录用户信息 /// /// - Returns: public func myInfo() -> O2LoginAccountModel? { return O2UserDefaults.shared.myInfo } /// 当前绑定单位服务器信息 /// /// - Returns: public func bindUnit() -> O2BindUnitModel? { return O2UserDefaults.shared.unit } /// 当前绑定的设备信息 /// /// - Returns: public func bindDevice() -> O2BindDeviceModel? { return O2UserDefaults.shared.device } /// 设置设备token 极光推送用的 /// /// - Parameter token: 极光推送deviceToken public func setDeviceToken(token: String) { O2UserDefaults.shared.deviceToken = token } /// 获取设备token /// /// - Returns: 极光推送deviceToken public func getDeviceToken() -> String { return O2UserDefaults.shared.deviceToken ?? "" } /// O2OA服务器信息 /// /// - Returns: public func centerServerInfo() -> O2CenterServerModel? { return O2UserDefaults.shared.centerServer } /// O2OA 服务器 各个微服务信息 /// /// - Parameter context: 微服务 /// - Returns: public func o2APIServer(context: O2ModuleContext) -> O2APIServerModel? { guard let dic = O2UserDefaults.shared.centerServer?.assembles else { return nil } switch context { case .x_processplatform_assemble_surface_script: return dic["x_processplatform_assemble_surface_script"] case .x_processplatform_assemble_surface_task: return dic["x_processplatform_assemble_surface_task"] case .x_processplatform_assemble_surface_worklog: return dic["x_processplatform_assemble_surface_worklog"] case .x_processplatform_assemble_surface_workcompleted: return dic["x_processplatform_assemble_surface_workcompleted"] case .x_processplatform_assemble_surface_attachment: return dic["x_processplatform_assemble_surface_attachment"] case .x_processplatform_assemble_surface_work: return dic["x_processplatform_assemble_surface_work"] case .x_file_assemble_control: return dic["x_file_assemble_control"] case .x_meeting_assemble_control: return dic["x_meeting_assemble_control"] case .x_attendance_assemble_control: return dic["x_attendance_assemble_control"] case .x_okr_assemble_control: return dic["x_okr_assemble_control"] case .x_bbs_assemble_control: return dic["x_bbs_assemble_control"] case .x_hotpic_assemble_control: return dic["x_hotpic_assemble_control"] case .x_processplatform_assemble_surface_applicationdict: return dic["x_processplatform_assemble_surface_applicationdict"] case .x_cms_assemble_control: return dic["x_cms_assemble_control"] case .x_organization_assemble_control: return dic["x_organization_assemble_control"] case .x_collaboration_assemble_websocket: return dic["x_collaboration_assemble_websocket"] case .x_organization_assemble_custom: return dic["x_organization_assemble_custom"] case .x_processplatform_assemble_surface: return dic["x_processplatform_assemble_surface"] case .x_processplatform_assemble_surface_read: return dic["x_processplatform_assemble_surface_read"] case .x_processplatform_assemble_surface_readcompleted: return dic["x_processplatform_assemble_surface_readcompleted"] case .x_organization_assemble_express: return dic["x_organization_assemble_express"] case .x_organization_assemble_personal: return dic["x_organization_assemble_personal"] case .x_processplatform_assemble_surface_taskcompleted: return dic["x_processplatform_assemble_surface_taskcompleted"] case .x_processplatform_assemble_surface_process: return dic["x_processplatform_assemble_surface_process"] case .x_component_assemble_control: return dic["x_component_assemble_control"] case .x_processplatform_assemble_surface_application: return dic["x_processplatform_assemble_surface_application"] case .x_processplatform_assemble_surface_data: return dic["x_processplatform_assemble_surface_data"] case .x_processplatform_assemble_designer: return dic["x_processplatform_assemble_designer"] case .x_processplatform_assemble_surface_review: return dic["x_processplatform_assemble_surface_review"] case .x_organization_assemble_authentication: return dic["x_organization_assemble_authentication"] case .x_portal_assemble_surface: return dic["x_portal_assemble_surface"] case .x_calendar_assemble_control: return dic["x_calendar_assemble_control"] case .x_jpush_assemble_control: return dic["x_jpush_assemble_control"] } } /// 移动端配置信息 /// /// - Returns: public func customStyle() -> O2CustomStyleModel? { return O2UserDefaults.shared.customStyle } /// 移动端配置hash值 /// /// - Returns: public func customStyleHash() -> String? { return O2UserDefaults.shared.customStyleHash } /// 移动端应用列表 /// /// - Returns: public func o2AppList() -> [O2AppModel] { var list: [O2AppModel] = [] if let nativeList = O2UserDefaults.shared.customStyle?.nativeAppList { for native in nativeList { let app = O2AppModel() app.id = native.key app.name = native.name app.portalUrl = "" app.portalCategory = "" app.type = O2ConfigInfo.O2_APP_TYPE_NATIVE app.enable = native.enable list.append(app) } } if let portalList = O2UserDefaults.shared.customStyle?.portalList { for portal in portalList { let app = O2AppModel() app.id = portal.id app.name = portal.name if let webserver = O2UserDefaults.shared.centerServer?.webServer { app.portalUrl = "\(webserver.httpProtocol ?? "http")://\(webserver.host ?? ""):\(webserver.port ?? 80)/\(O2ConfigInfo.O2_DESKTOP_CONTEXT)/appMobile.html?app=portal.Portal&status={\"portalId\":\"\(portal.id ?? "")\"}" } app.portalCategory = portal.portalCategory app.type = O2ConfigInfo.O2_APP_TYPE_PORTAL app.enable = portal.enable list.append(app) } } return list } //MARK: - public 公开方法 /// 启动 /// 整个过程包括绑定信息检查,客户端信息检查,登录检查 /// - Parameter callback: O2LaunchProcessState public func launch(callback: @escaping (_ state: O2LaunchProcessState, _ msg: String?)->()) { _ = checkBindUnit() // .flatMap { (device) -> Observable in // return self.checkBindUnitIsExpired(device: device) // } .flatMap { (unit) -> Observable in // let o2Res = response.mapObject(BaseO2ResponseData.self) // if o2Res?.isSuccess() == false { // throw O2AuthError.bindExpireError // }else { // guard let unit = o2Res?.data else { // throw O2AuthError.bindExpireError // } // // 每次验证完成后更新unit 信息 , 连接unit的中心服务器 // O2UserDefaults.shared.unit = unit // return self.connectToCenterServer(unit: unit) // } return self.connectToCenterServer(unit: unit) }.flatMap({ (response) -> Observable in let result = response.mapObject(BaseO2ResponseData.self) if let centerServer = result?.data { let unit = O2UserDefaults.shared.unit let httpProtocol = unit?.httpProtocol ?? "http" centerServer.assembles?.forEach({ (key,value) in value.httpProtocol = httpProtocol centerServer.assembles![key] = value }) centerServer.webServer?.httpProtocol = httpProtocol // 保存中心服务器数据 O2UserDefaults.shared.centerServer = centerServer return self.checkCustomStyleNeedUpdate(unit: unit!) }else { throw O2AuthError.blockError("无法连接中心服务器!") } }).flatMap { (response) -> Observable in let o2Res = response.mapObject(BaseO2ResponseData.self) let currentHash = O2UserDefaults.shared.customStyleHash ?? "" if o2Res?.isSuccess() == false { throw O2AuthError.blockError("检查移动端配置是否需要更新时出错, \(o2Res?.message ?? "")") }else { if let newHash = o2Res?.data?.value { if newHash == currentHash { // 没有更新不需要下载 return self.boolObservableCreate(true) }else { //保存这个hash码,并下载 O2UserDefaults.shared.customStyleHash = newHash return self.downloadCustomStyle() } }else { throw O2AuthError.blockError("检查移动端配置是否需要更新时出错, 没有获取到hash值") } } }.flatMap { (result) -> Observable in if result { if let token = O2UserDefaults.shared.myInfo?.token { return self.loginWithToken(token) }else { guard let mobile = O2UserDefaults.shared.device?.mobile, let code = O2UserDefaults.shared.device?.code else { throw O2AuthError.noLoginError } return self.loginWithPhoneCode(mobile, code) } }else { throw O2AuthError.blockError("下载移动端配置时出错!!!") } }.flatMap { (response) -> Observable in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo return self.boolObservableCreate(true) }else { throw O2AuthError.noLoginError } }else { throw O2AuthError.noLoginError } } .subscribe { (event) in switch event { case .next(let element): if element { callback(O2LaunchProcessState.success, nil) }else { callback(O2LaunchProcessState.loginError, "登录失败!") } break case .error(let error): if error is O2AuthError { let aError = error as! O2AuthError switch aError { case .noLoginError: callback(O2LaunchProcessState.loginError, "登录失败!") break case .noBindError: callback(O2LaunchProcessState.bindError, "绑定失败!") break case .bindExpireError: callback(O2LaunchProcessState.bindError, "绑定过期!") break case .blockError(let msg): callback(O2LaunchProcessState.unknownError, msg) break } }else { callback(O2LaunchProcessState.unknownError, "未知错误,\(String(describing: error))") } break case .completed: break } } } /// 内网版本启动入口 /// /// - Parameters: /// - unit: 内网服务器信息 /// - callback: O2LaunchProcessState public func launchInner(unit: O2BindUnitModel, callback: @escaping (_ state: O2LaunchProcessState, _ msg: String?)->()) { // 保存数据 连接unit的中心服务器 O2UserDefaults.shared.unit = unit _ = connectToCenterServer(unit: unit).flatMap { (response) -> Observable in let result = response.mapObject(BaseO2ResponseData.self) if let centerServer = result?.data { let httpProtocol = unit.httpProtocol ?? "http" centerServer.assembles?.forEach({ (key,value) in value.httpProtocol = httpProtocol centerServer.assembles![key] = value }) centerServer.webServer?.httpProtocol = httpProtocol // 保存中心服务器数据 O2UserDefaults.shared.centerServer = centerServer return self.checkCustomStyleNeedUpdate(unit: unit) }else { throw O2AuthError.blockError("无法连接中心服务器!") } } .flatMap { (response) -> Observable in let o2Res = response.mapObject(BaseO2ResponseData.self) let currentHash = O2UserDefaults.shared.customStyleHash ?? "" if o2Res?.isSuccess() == false { throw O2AuthError.blockError("检查移动端配置是否需要更新时出错, \(o2Res?.message ?? "")") }else { if let newHash = o2Res?.data?.value { if newHash == currentHash { // 没有更新不需要下载 return self.boolObservableCreate(true) }else { //保存这个hash码,并下载 O2UserDefaults.shared.customStyleHash = newHash return self.downloadCustomStyle() } }else { throw O2AuthError.blockError("检查移动端配置是否需要更新时出错, 没有获取到hash值") } } }.flatMap { (result) -> Observable in if result { if let token = O2UserDefaults.shared.myInfo?.token { return self.loginWithToken(token) }else { guard let mobile = O2UserDefaults.shared.device?.mobile, let code = O2UserDefaults.shared.device?.code else { throw O2AuthError.noLoginError } return self.loginWithPhoneCode(mobile, code) } }else { throw O2AuthError.blockError("下载移动端配置时出错!!!") } }.flatMap { (response) -> Observable in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo return self.boolObservableCreate(true) }else { throw O2AuthError.noLoginError } }else { throw O2AuthError.noLoginError } }.subscribe { (event) in switch event { case .next(let element): if element { callback(O2LaunchProcessState.success, nil) }else { callback(O2LaunchProcessState.loginError, "登录失败!") } break case .error(let error): if error is O2AuthError { let aError = error as! O2AuthError switch aError { case .noLoginError: callback(O2LaunchProcessState.loginError, "登录失败!") break case .blockError(let msg): callback(O2LaunchProcessState.unknownError, msg) break default : callback(O2LaunchProcessState.unknownError, "") break } }else { callback(O2LaunchProcessState.unknownError, "未知错误,\(String(describing: error))") } break case .completed: break } } } /// 登录 手机号码 验证码 /// /// - Parameters: /// - mobile: 手机号码 /// - code: 验证码 /// - callback: 登录结果 public func login(mobile: String, code: String, callback: @escaping (_ result: Bool, _ error: String?) ->() ) { _ = self.loginWithPhoneCode(mobile, code) .subscribe(onNext: { (response) in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo callback(true, nil) }else { callback(false, "登录失败,用户名是: 找不到该用户信息 ! ") } }else { callback(false, "登录失败,\(account?.message ?? "")") } }, onError: { (error) in callback(false, "登录失败,\(String(describing: error))") }, onCompleted: { print("login completed!") }) { print("login finish!") } } /// 登录 扫码登录 public func loginWithScanCode(meta: String, callback: @escaping (_ result: Bool, _ error: String?) ->()) { _ = self.loginAPI.rx.request(.loginWithScanCode(meta)).asObservable().subscribe(onNext: { (response) in let back = response.mapObject(BaseO2ResponseData.self) if back?.isSuccess() == true { if let v = back?.data, v.value == true { callback(true, nil) }else { callback(false, "登录失败!") } }else { callback(false, "登录失败,\(back?.message ?? "")") } }, onError: { (error) in callback(false, "登录失败,\(String(describing: error))") }, onCompleted: { print("loginWithScanCode completed!") }) { print("loginWithScanCode finish!") } } public func loginWithPassword(username: String, password: String, callback: @escaping (_ result: Bool, _ error: String?) ->()) { _ = self.loginWithUsernamePassword(username, password) .subscribe(onNext: { (response) in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo callback(true, nil) }else { callback(false, "登录失败,用户名是: 找不到该用户信息 ! ") } }else { callback(false, "登录失败,\(account?.message ?? "")") } }, onError: { (error) in callback(false, "登录失败,\(String(describing: error))") }, onCompleted: { print("login completed!") }) { print("login finish!") } } /// 人脸识别登录 /// /// - Parameters: /// - userId: 用户uid /// - callback: public func faceRecognizeLogin(userId: String, callback: @escaping (_ result: Bool, _ error: String?) ->()) { _ = self.desEncrypt(userId).flatMap { (token) -> Observable in return self.loginWithSSO(token) }.subscribe(onNext: { (response) in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo callback(true, nil) }else { callback(false, "登录失败,找不到该用户信息 ! ") } }else { callback(false, "登录失败,\(account?.message ?? "")") } }, onError: { (error) in callback(false, "登录失败,\(String(describing: error))") }, onCompleted: nil, onDisposed: nil) } /// 登出 清除当前登录的用户信息 /// /// - Parameter callback: public func logout(callback: @escaping (_ result: Bool, _ error: String?) ->()) { _ = Observable.create { (observer) -> Disposable in O2UserDefaults.shared.myInfo = nil let device = O2UserDefaults.shared.device device?.code = nil O2UserDefaults.shared.device = device observer.onNext(true) observer.onCompleted() return Disposables.create() }.subscribe(onNext: { (result) in callback(result, nil) }, onError: { (error) in callback(false, "\(String(describing: error))") }, onCompleted: nil, onDisposed: nil) } /// 解除绑定前 清空本地数据 /// /// - Parameter callback: 返回结果 public func clearAllInformationBeforeReBind(callback: @escaping (_ result: Bool, _ error: String?) ->()) { _ = Observable.create { (observer) -> Disposable in //清除本地关于账号 单位等信息 O2UserDefaults.shared.unit = nil O2UserDefaults.shared.device = nil O2UserDefaults.shared.myInfo = nil O2UserDefaults.shared.customStyle = nil O2UserDefaults.shared.customStyleHash = nil O2UserDefaults.shared.centerServer = nil observer.onNext(true) observer.onCompleted() return Disposables.create() }.subscribe(onNext: { (result) in callback(result, nil) }, onError: { (error) in callback(false, "\(String(describing: error))") }, onCompleted: nil, onDisposed: nil) } /// 登录的时候 发送短信验证码 /// /// - Parameters: /// - mobile: 手机号码 /// - callback: public func sendLoginSMS(mobile: String, callback: @escaping (_ result: Bool, _ error: String?) ->()) { _ = loginAPI.rx.request(.createLoginCode(mobile)).asObservable() .subscribe(onNext: { (response) in let account = response.mapObject(BaseO2ResponseData.self) if let result = account?.data?.value, result == true { callback(true, nil) }else { callback(false, "发送短信失败!") } }, onError: { (error) in callback(false, "发送短信失败!\(error)") }, onCompleted: nil, onDisposed: nil) } /// 绑定手机的时候 发送短信验证码 /// /// - Parameters: /// - mobile: 手机号码 /// - callback: public func sendBindSMS(mobile: String, callback: @escaping (_ result: Bool, _ error: String?) ->()) { let req = O2NodeReqModel() req.mobile = mobile _ = registerAPI.rx.request(.generateVerifiyCode(req)).asObservable() .subscribe(onNext: { (response) in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { callback(true, nil) }else { callback(false, "发送短信失败!") } }, onError: { (error) in callback(false, "发送短信失败!\(error)") }, onCompleted: nil, onDisposed: nil) } /// 绑定到sample 服务器,苹果上架使用 /// - Parameters: /// - mobile: 手机号码 public func bindSampleServer(mobile: String, callback: @escaping (_ result: O2BindProcessState, _ error: String?) ->()) { //填入sample的服务器信息 let _ = Observable.create { (observer) -> Disposable in let unit: O2BindUnitModel = O2BindUnitModel() unit.id = "61a4d035-81ee-44a6-af3b-ab3d374ee24d" unit.name = "演示站点" unit.pinyin = "yanshizhandian" unit.pinyinInitial = "yszd" unit.centerHost = "sample.o2oa.net" unit.centerPort = 40030 unit.centerContext = "/x_program_center" unit.httpProtocol = "https" O2UserDefaults.shared.unit = unit observer.onNext(unit) observer.onCompleted() return Disposables.create() }.flatMap { (unit) -> Observable in let bindDevice: O2BindDeviceModel = O2BindDeviceModel() bindDevice.unit = unit.name bindDevice.name = O2UserDefaults.shared.deviceToken bindDevice.mobile = mobile bindDevice.code = "" bindDevice.deviceType = "ios" O2UserDefaults.shared.device = bindDevice return self.connectToCenterServer(unit: unit) }.flatMap({ (response) -> Observable in let result = response.mapObject(BaseO2ResponseData.self) if let centerServer = result?.data { let unit = O2UserDefaults.shared.unit let httpProtocol = unit?.httpProtocol ?? "http" centerServer.assembles?.forEach({ (key,value) in value.httpProtocol = httpProtocol centerServer.assembles![key] = value }) centerServer.webServer?.httpProtocol = httpProtocol // 保存中心服务器数据 O2UserDefaults.shared.centerServer = centerServer return self.checkCustomStyleNeedUpdate(unit: unit!) }else { O2UserDefaults.shared.unit = nil O2UserDefaults.shared.device = nil throw O2BindDiscontinue.unknownError("无法连接中心服务器!") } }).flatMap { (response) -> Observable in let o2Res = response.mapObject(BaseO2ResponseData.self) let currentHash = O2UserDefaults.shared.customStyleHash ?? "" if o2Res?.isSuccess() == false { throw O2BindDiscontinue.noLoginError("检查移动端配置是否需要更新时出错, \(o2Res?.message ?? "")") }else { if let newHash = o2Res?.data?.value { if newHash == currentHash { // 没有更新不需要下载 return self.boolObservableCreate(true) }else { //保存这个hash码,并下载 O2UserDefaults.shared.customStyleHash = newHash return self.downloadCustomStyle() } }else { throw O2BindDiscontinue.noLoginError("检查移动端配置是否需要更新时出错, 没有获取到hash值") } } }.flatMap { (result) -> Observable in if result { //sample的默认密码 guard let mobile = O2UserDefaults.shared.device?.mobile else { throw O2BindDiscontinue.noLoginError("登录失败!") } return self.loginWithUsernamePassword(mobile, "o2") // if let token = O2UserDefaults.shared.myInfo?.token { // return self.loginWithToken(token) // }else { // guard let mobile = O2UserDefaults.shared.device?.mobile, let code = O2UserDefaults.shared.device?.code else { // throw O2BindDiscontinue.noLoginError("登录失败!") // } // return self.loginWithPhoneCode(mobile, code) // } }else { throw O2BindDiscontinue.noLoginError("下载移动端配置时出错!!!") } }.flatMap { (response) -> Observable in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo return self.boolObservableCreate(true) }else { throw O2BindDiscontinue.noLoginError("登录失败!") } }else { throw O2BindDiscontinue.noLoginError("登录失败!") } }.subscribe(onNext: { (result) in if result { callback(O2BindProcessState.success, nil) }else { callback(O2BindProcessState.goToLogin, "登录失败!") } }, onError: { (error) in if error is O2BindDiscontinue { let bError = error as! O2BindDiscontinue switch bError { case O2BindDiscontinue.tooManyUnit(let list): callback(O2BindProcessState.goToChooseBindServer(list), nil) break case O2BindDiscontinue.noLoginError(let msg): callback(O2BindProcessState.goToLogin, msg) break case O2BindDiscontinue.noUnitCanBindError: callback(O2BindProcessState.noUnitCanBindError, nil) break case O2BindDiscontinue.unknownError(let msg): callback(O2BindProcessState.unknownError, msg) break } }else { callback(O2BindProcessState.unknownError, "\(error)") } }, onCompleted: nil, onDisposed: nil) } /// 绑定手机号码到服务器, 并且完成全部的登录过程。 /// 第一步绑定的时候用的,如果单位列表大于1个就抛异常, 外面接收到这个特殊异常就走第二步选择单位 /// /// - Parameters: /// - mobile: 手机号码 /// - code: 验证码 /// - callback: public func bindMobileToSever(mobile: String, code: String, callback: @escaping (_ result: O2BindProcessState, _ error: String?) ->()) { let bindDevice: O2BindDeviceModel = O2BindDeviceModel() let req = O2NodeReqModel() req.mobile = mobile req.value = code _ = registerAPI.rx.request(.verifiyCode(req)).asObservable() .flatMap { (response) -> Observable in let listResult = response.mapObject(BaseO2ResponseData<[O2BindUnitModel]>.self) if let list = listResult?.data, list.count > 0 { if list.count > 1 { throw O2BindDiscontinue.tooManyUnit(list) }else { // 保存单位信息 O2UserDefaults.shared.unit = list[0] bindDevice.unit = list[0].name bindDevice.name = O2UserDefaults.shared.deviceToken bindDevice.mobile = mobile bindDevice.code = code return self.bindToCollect(device: bindDevice) } }else { throw O2BindDiscontinue.noUnitCanBindError } } .flatMap { (response) -> Observable in let idData = response.mapObject(BaseO2ResponseData.self) if idData?.isSuccess() == true { // 保存device O2UserDefaults.shared.device = bindDevice let unit = O2UserDefaults.shared.unit return self.connectToCenterServer(unit: unit!) }else { O2UserDefaults.shared.unit = nil throw O2BindDiscontinue.unknownError("绑定失败!") } }.flatMap({ (response) -> Observable in let result = response.mapObject(BaseO2ResponseData.self) if let centerServer = result?.data { let unit = O2UserDefaults.shared.unit let httpProtocol = unit?.httpProtocol ?? "http" centerServer.assembles?.forEach({ (key,value) in value.httpProtocol = httpProtocol centerServer.assembles![key] = value }) centerServer.webServer?.httpProtocol = httpProtocol // 保存中心服务器数据 O2UserDefaults.shared.centerServer = centerServer return self.checkCustomStyleNeedUpdate(unit: unit!) }else { O2UserDefaults.shared.unit = nil O2UserDefaults.shared.device = nil throw O2BindDiscontinue.unknownError("无法连接中心服务器!") } }).flatMap { (response) -> Observable in let o2Res = response.mapObject(BaseO2ResponseData.self) let currentHash = O2UserDefaults.shared.customStyleHash ?? "" if o2Res?.isSuccess() == false { throw O2BindDiscontinue.noLoginError("检查移动端配置是否需要更新时出错, \(o2Res?.message ?? "")") }else { if let newHash = o2Res?.data?.value { if newHash == currentHash { // 没有更新不需要下载 return self.boolObservableCreate(true) }else { //保存这个hash码,并下载 O2UserDefaults.shared.customStyleHash = newHash return self.downloadCustomStyle() } }else { throw O2BindDiscontinue.noLoginError("检查移动端配置是否需要更新时出错, 没有获取到hash值") } } }.flatMap { (result) -> Observable in if result { if let token = O2UserDefaults.shared.myInfo?.token { return self.loginWithToken(token) }else { guard let mobile = O2UserDefaults.shared.device?.mobile, let code = O2UserDefaults.shared.device?.code else { throw O2BindDiscontinue.noLoginError("登录失败!") } return self.loginWithPhoneCode(mobile, code) } }else { throw O2BindDiscontinue.noLoginError("下载移动端配置时出错!!!") } }.flatMap { (response) -> Observable in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo return self.boolObservableCreate(true) }else { throw O2BindDiscontinue.noLoginError("登录失败!") } }else { throw O2BindDiscontinue.noLoginError("登录失败!") } }.subscribe(onNext: { (result) in if result { callback(O2BindProcessState.success, nil) }else { callback(O2BindProcessState.goToLogin, "登录失败!") } }, onError: { (error) in if error is O2BindDiscontinue { let bError = error as! O2BindDiscontinue switch bError { case O2BindDiscontinue.tooManyUnit(let list): callback(O2BindProcessState.goToChooseBindServer(list), nil) break case O2BindDiscontinue.noLoginError(let msg): callback(O2BindProcessState.goToLogin, msg) break case O2BindDiscontinue.noUnitCanBindError: callback(O2BindProcessState.noUnitCanBindError, nil) break case O2BindDiscontinue.unknownError(let msg): callback(O2BindProcessState.unknownError, msg) break } }else { callback(O2BindProcessState.unknownError, "\(error)") } }, onCompleted: nil, onDisposed: nil) } /// 绑定手机到服务器,并完成全部的登录过程 /// /// - Parameters: /// - unit: 绑定的单位服务器对象 /// - mobile: 手机号码 /// - code: 验证码 /// - callback: public func bindMobileToServer(unit: O2BindUnitModel, mobile: String, code: String, callback: @escaping (_ result: O2BindProcessState, _ error: String?) ->()) { // 保存单位信息 O2UserDefaults.shared.unit = unit let bindDevice: O2BindDeviceModel = O2BindDeviceModel() bindDevice.unit = unit.name bindDevice.name = O2UserDefaults.shared.deviceToken bindDevice.mobile = mobile bindDevice.code = code bindDevice.deviceType = "ios" _ = self.bindToCollect(device: bindDevice) .flatMap { (response) -> Observable in let idData = response.mapObject(BaseO2ResponseData.self) if idData?.isSuccess() == true { // 保存device O2UserDefaults.shared.device = bindDevice let unit = O2UserDefaults.shared.unit return self.connectToCenterServer(unit: unit!) }else { O2UserDefaults.shared.unit = nil throw O2BindDiscontinue.unknownError("绑定失败!") } }.flatMap({ (response) -> Observable in let result = response.mapObject(BaseO2ResponseData.self) if let centerServer = result?.data { let unit = O2UserDefaults.shared.unit let httpProtocol = unit?.httpProtocol ?? "http" centerServer.assembles?.forEach({ (key,value) in value.httpProtocol = httpProtocol centerServer.assembles![key] = value }) centerServer.webServer?.httpProtocol = httpProtocol // 保存中心服务器数据 O2UserDefaults.shared.centerServer = centerServer return self.checkCustomStyleNeedUpdate(unit: unit!) }else { O2UserDefaults.shared.unit = nil O2UserDefaults.shared.device = nil throw O2BindDiscontinue.unknownError("无法连接中心服务器!") } }).flatMap { (response) -> Observable in let o2Res = response.mapObject(BaseO2ResponseData.self) let currentHash = O2UserDefaults.shared.customStyleHash ?? "" if o2Res?.isSuccess() == false { throw O2BindDiscontinue.noLoginError("检查移动端配置是否需要更新时出错, \(o2Res?.message ?? "")") }else { if let newHash = o2Res?.data?.value { if newHash == currentHash { // 没有更新不需要下载 return self.boolObservableCreate(true) }else { //保存这个hash码,并下载 O2UserDefaults.shared.customStyleHash = newHash return self.downloadCustomStyle() } }else { throw O2BindDiscontinue.noLoginError("检查移动端配置是否需要更新时出错, 没有获取到hash值") } } }.flatMap { (result) -> Observable in if result { if let token = O2UserDefaults.shared.myInfo?.token { return self.loginWithToken(token) }else { guard let mobile = O2UserDefaults.shared.device?.mobile, let code = O2UserDefaults.shared.device?.code else { throw O2BindDiscontinue.noLoginError("登录失败!") } return self.loginWithPhoneCode(mobile, code) } }else { throw O2BindDiscontinue.noLoginError("下载移动端配置时出错!!!") } }.flatMap { (response) -> Observable in let account = response.mapObject(BaseO2ResponseData.self) if account?.isSuccess() == true { if let myInfo = account?.data, myInfo.name != "anonymous" { O2UserDefaults.shared.myInfo = myInfo return self.boolObservableCreate(true) }else { throw O2BindDiscontinue.noLoginError("登录失败!") } }else { throw O2BindDiscontinue.noLoginError("登录失败!") } }.subscribe(onNext: { (result) in if result { callback(O2BindProcessState.success, nil) }else { callback(O2BindProcessState.goToLogin, "登录失败!") } }, onError: { (error) in if error is O2BindDiscontinue { let bError = error as! O2BindDiscontinue switch bError { case O2BindDiscontinue.tooManyUnit(let list): callback(O2BindProcessState.goToChooseBindServer(list), nil) break case O2BindDiscontinue.noLoginError(let msg): callback(O2BindProcessState.goToLogin, msg) break case O2BindDiscontinue.noUnitCanBindError: callback(O2BindProcessState.noUnitCanBindError, nil) break case O2BindDiscontinue.unknownError(let msg): callback(O2BindProcessState.unknownError, msg) break } }else { callback(O2BindProcessState.unknownError, "\(error)") } }, onCompleted: nil, onDisposed: nil) } /// 绑定的设备列表 /// /// - Parameters: /// - unitId: 绑定的单位id /// - mobile: 手机号码 /// - token: 设备号 O2BindDeviceModel.name /// - callback: public func bindDeviceList(unitId: String, mobile: String, token: String, callback: @escaping (_ list: [O2BindDeviceModel], _ error: String?)->Void) { _ = registerAPI.rx.request(.deviceList(unitId, mobile, token)).asObservable() .subscribe(onNext: { (response) in let result = response.mapObject(BaseO2ResponseData<[O2BindDeviceModel]>.self) if result?.isSuccess() == true { callback(result?.data ?? [], nil) }else { let message = "查询设备失败,\(result?.message ?? "")" callback([], message) } }, onError: { (error) in let message = "查询设备异常,\(error.localizedDescription)" callback([], message) }, onCompleted: nil, onDisposed: nil) } /// 解除设备绑定 /// /// - Parameters: /// - device: 设备对象 主要是name 就是设备号 /// - callback: public func unBindFromCollect(deviceId: String, callback: @escaping (_ result:Bool, _ error:String?)->Void) { let device: O2BindDeviceModel = O2BindDeviceModel() device.name = deviceId _ = registerAPI.rx.request(.unBindFromDevice(device)).asObservable() .subscribe(onNext: { (response) in let result = response.mapObject(BaseO2ResponseData.self) if result?.isSuccess() == true { callback(true, nil) }else { let message = "解除绑定失败,\(result?.message ?? "")" callback(false, message) } }, onError: { (error) in let message = "解除绑定异常,\(error.localizedDescription)" callback(false, message) }, onCompleted: nil, onDisposed: nil) } //MARK: - private private func boolObservableCreate(_ result: Bool) -> Observable { return Observable.create({ (observer) -> Disposable in observer.on(.next(result)) observer.on(.completed) return Disposables.create() }) } /// 检查是否绑定过 /// /// - Returns: private func checkBindUnit() -> Observable { // return Observable.create({ (any) -> Disposable in // if let device = O2UserDefaults.shared.device { // any.onNext(device) // any.onCompleted() // }else { // any.onError(O2AuthError.noBindError) // } // return Disposables.create() // }) return Observable.create ({ (any) -> Disposable in if let unit = O2UserDefaults.shared.unit { any.onNext(unit) any.onCompleted() } else { any.onError(O2AuthError.noBindError) } return Disposables.create() }) } /// 检查当前绑定信息是否过期 /// /// - Parameter device: 手机号码等设备信息 /// - Returns: private func checkBindUnitIsExpired(device: O2BindDeviceModel) -> Observable { return registerAPI.rx.request(.queryBindInfo(device)).asObservable() } /// 连接到O2OA中心服务器 /// /// - Parameter unit: 单位服务器信息 /// - Returns: private func connectToCenterServer(unit: O2BindUnitModel) -> Observable { return registerAPI.rx.request(.downloadNodeAPI(unit)).asObservable() } /// 检查移动端配置信息是否有更新 /// /// - Parameter unit: 当前绑定的党委 /// - Returns: private func checkCustomStyleNeedUpdate(unit: O2BindUnitModel) -> Observable { return registerAPI.rx.request(.verConfigInfo(unit)).asObservable() } /// 下载移动端配置信息 /// /// - Returns: private func downloadCustomStyle() -> Observable { if let unit = O2UserDefaults.shared.unit { return registerAPI.rx.request(.downloadConfigInfo(unit)) .asObservable() .flatMap { (response) -> Observable in let result = response.mapObject(BaseO2ResponseData.self) if result?.isSuccess() == true { //保存移动端配置信息 O2UserDefaults.shared.customStyle = result?.data return self.boolObservableCreate(true) }else { throw O2AuthError.blockError("下载移动端配置时出错, \(result?.message ?? "")") } } }else { return self.boolObservableCreate(false) } } /// token登录 /// /// - Parameter token: 用户登录的token /// - Returns: private func loginWithToken(_ token: String) -> Observable { return loginAPI.rx.request(.loginWithToken(token)).asObservable() } /// 手机验证码登录 /// /// - Parameters: /// - mobile: 手机号码 /// - code: 验证码 /// - Returns: private func loginWithPhoneCode(_ mobile: String, _ code: String) -> Observable { return loginAPI.rx.request(.loginWithCredntial(mobile, code)).asObservable() } /// 用户名密码登录 /// /// - Parameters: /// - username: 用户名 /// - password: 密码 /// - Returns: private func loginWithUsernamePassword(_ username: String, _ password: String) -> Observable { return loginAPI.rx.request(.loginWithPassword(username, password)).asObservable() } /// sso 登录 /// /// - Parameter token: 加密后的token /// - Returns: private func loginWithSSO(_ token: String) -> Observable { return loginAPI.rx.request(.loginWithSSO(O2ConfigInfo.O2_OA_SSO_CLIENT, token)).asObservable() } /// 绑定到wcollect服务器 /// /// - Parameters: /// - device: 设备对象 /// - Returns: private func bindToCollect(device: O2BindDeviceModel) -> Observable { return registerAPI.rx.request(.bindMobileToDevice(device)).asObservable() } /// des 加密 /// /// - Parameter uid: /// - Returns: private func desEncrypt(_ uid: String) -> Observable { return Observable.create({ (observer) -> Disposable in do { let en = SymmetricCryptor(algorithm: .des, options: (kCCOptionECBMode + kCCOptionPKCS7Padding)) en.setRandomIV() let timeInterval = Date().timeIntervalSince1970 let time = CLongLong(round(timeInterval*1000)) let code = "\(uid)#\(time)" print("code: \(code)") let encrypt = try en.crypt(string: code, key: O2ConfigInfo.O2_OA_DES_KEY) let base64 = encrypt.base64EncodedString() print("加密后的字符串:\(base64)") let decrypt = try en.decrypt(encrypt, key: O2ConfigInfo.O2_OA_DES_KEY) let deString = String(data: decrypt, encoding: String.Encoding.utf8) print("解密后的字符串:\(deString ?? "")") let first = base64.replacingOccurrences(of: "+", with: "-") let second = first.replacingOccurrences(of: "/", with: "_") let token = second.replacingOccurrences(of: "=", with: "") print("安全替换后的字符串:\(token)") observer.onNext(token) }catch { print("加密异常,\(error)") observer.onError(error) } observer.onCompleted() return Disposables.create() }) } }