CheckMobileViewController.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. //
  2. // CheckMobileViewController.swift
  3. // MTP2_iOS
  4. //
  5. // Created by Muchinfo on 2018/11/15.
  6. // Copyright © 2018 Muchinfo. All rights reserved.
  7. //
  8. import UIKit
  9. import NVActivityIndicatorView
  10. import WHToast
  11. class CheckMobileViewController: BaseViewController {
  12. private let TAG_BACK = 100
  13. private let TAG_CAPTCHA = 101
  14. private let TAG_COMMIT = 102
  15. private let TAG_MOBILEAREACODE = 103
  16. /// 导航栏标题
  17. @IBOutlet weak var navTitle: UINavigationItem!
  18. /// 手机号码输入框
  19. @IBOutlet weak var mobileTextField: UITextField! {
  20. didSet {
  21. mobileTextField.attributedPlaceholder = "pleaseEnterPhoneNumber".withTextColor(Color_placeholder_text)
  22. }
  23. }
  24. /// 区号按钮
  25. @IBOutlet weak var areaButton: UIButton! {
  26. didSet {
  27. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+0.2) {
  28. self.areaButton.layer.masksToBounds = false
  29. self.areaButton.layer.cornerRadius = 0
  30. }
  31. }
  32. }
  33. /// 验证码输入框
  34. @IBOutlet weak var captchaTextField: UITextField! {
  35. didSet {
  36. captchaTextField.attributedPlaceholder = "请输入手机验证码".withTextColor(Color_placeholder_text)
  37. }
  38. }
  39. /// 提交按钮
  40. @IBOutlet weak var commitButton: UIButton!
  41. /// 验证码按钮
  42. @IBOutlet weak var captchaButton: UIButton!
  43. /// 定时器
  44. @IBOutlet weak var timerLabel: UILabel!
  45. /// 高度约束
  46. @IBOutlet weak var referralLayoutConstraint: NSLayoutConstraint!
  47. /// 推荐码
  48. @IBOutlet weak var referralField: UITextField! {
  49. didSet {
  50. referralField.attributedPlaceholder = "请输入推荐码".withTextColor(Color_placeholder_text)
  51. }
  52. }
  53. /// 登录类型
  54. var type = CheckMobileType.MobileSignin
  55. /// 三方登录的OpenID, 注册业务必需
  56. var openID: String?
  57. var accessToken: String?
  58. /// 三方登录类型,“1”为微信;“2”为支付宝。注册业务必需
  59. var authType: String?
  60. /// 手机区号 默认为+86
  61. var areaCode: String = "+86"
  62. /// 是否需要显示推荐码 0不需要显示推荐码
  63. var isShowRefer: Int = 0 {
  64. didSet {
  65. referralLayoutConstraint.constant = isShowRefer == 0 ? 0.0 : 55.0
  66. }
  67. }
  68. /// 短信验证码
  69. lazy var imageCodeViewController: ImageCodeViewController = {
  70. $0.popBlock = { [weak self] (_ takeInfo: String?) in
  71. /// 发送验证码
  72. self?.sendCaptcha()
  73. }
  74. return $0
  75. } (viewController(name: "ResetPwd", identifier: "ImageCodeID") as! ImageCodeViewController)
  76. /// 定时器相关
  77. private let captchaTimerID = "captchaTimerID"
  78. private var captchaCD = 60
  79. // MARK: - 生命周期相关
  80. override func viewDidLoad() {
  81. super.viewDidLoad()
  82. /// UI界面初始化
  83. buildView()
  84. }
  85. deinit {
  86. /// 销毁定时器
  87. MCGCDTimer.shared.cancleTimer(WithTimerName: self.captchaTimerID)
  88. }
  89. // MARK: - 生命周期相关
  90. /// UI界面初始化
  91. private func buildView() {
  92. switch type {
  93. case .AuthSignup: // 三方登录注册业务
  94. navTitle.title = "手机验证"
  95. commitButton.setTitle("验证", for: .normal)
  96. case .MobileSignin: // 手机号码登录
  97. navTitle.title = "手机登录"
  98. commitButton.setTitle("登录", for: .normal)
  99. }
  100. /// 获取上次登录的手机账号
  101. guard let phone = UserDefaultsUtils.getLoginPhone(), phone != "" else { return }
  102. mobileTextField.text = phone
  103. }
  104. // MARK: - 交互相关
  105. @IBAction func onViewPressed(_ sender: UIControl) {
  106. switch sender.tag {
  107. case TAG_BACK:
  108. self.navigationController?.dismiss(animated: true, completion: nil)
  109. case TAG_CAPTCHA: // 发送短信验证码
  110. /// 手机号码
  111. guard let mobile = mobileTextField.text, mobile != "" else {
  112. WHToast.showError(withMessage: "请输入手机号码", duration: 1.5, finishHandler: {})
  113. return
  114. }
  115. self.present(imageCodeViewController, animated: true, completion: {})
  116. case TAG_MOBILEAREACODE: /// 手机区号
  117. let mobileArearCodeViewController = viewController(name: "ResetPwd", identifier: "MobileAreaCodeID") as! MobileAreaCodeViewController
  118. /// 设置手机区号
  119. mobileArearCodeViewController.popBlock = { [weak self] (_ code: String) in
  120. DispatchQueue.main.async {
  121. self?.areaCode = code
  122. self?.areaButton.setTitle(code, for: .normal)
  123. }
  124. }
  125. self.navigationController?.pushViewController(mobileArearCodeViewController, animated: true)
  126. case TAG_COMMIT: // 提交
  127. self.view.endEditing(true)
  128. guard let mobile = mobileTextField.text, mobile != "" else {
  129. WHToast.showError(withMessage: "pleaseentercorrectmobilenumber", duration: 1.0, finishHandler: {})
  130. return
  131. }
  132. guard let captcha = captchaTextField.text, captcha.trimmingCharacters(in: .whitespaces) != "" else {
  133. WHToast.showError(withMessage: "pleaseEnterCheckCode", duration: 1.0, finishHandler: {})
  134. return
  135. }
  136. if self.type == .AuthSignup {
  137. // 三方登录注册业务
  138. authSignup(nil)
  139. } else {
  140. // 手机号码登录
  141. mobileLogin(nil)
  142. }
  143. default:
  144. break
  145. }
  146. }
  147. // MARK: - 验证码相关
  148. /// 设置验证码按钮状态的方法
  149. ///
  150. /// - Parameter enable: 是否可用,如为不可用则开始60s倒计时
  151. private func setCaptcha(enable: Bool) {
  152. if enable {
  153. MCGCDTimer.shared.cancleTimer(WithTimerName: self.captchaTimerID)
  154. DispatchQueue.main.async {
  155. self.captchaButton.isEnabled = true
  156. self.captchaButton.setTitle("获取验证码", for: .normal)
  157. self.timerLabel.isHidden = true
  158. }
  159. } else {
  160. DispatchQueue.main.async {
  161. self.captchaCD = 60
  162. self.captchaButton.isEnabled = false
  163. self.captchaButton.setTitle("", for: .disabled)
  164. self.timerLabel.text = "\(self.captchaCD)秒"
  165. self.timerLabel.isHidden = false
  166. self.captchaTextField.becomeFirstResponder()
  167. MCGCDTimer.shared.cancleTimer(WithTimerName: self.captchaTimerID)
  168. MCGCDTimer.shared.scheduledDispatchTimer(WithTimerName: self.captchaTimerID, timeInterval: 1.0, queue: .main, repeats: true, isNowRun: false) {
  169. DispatchQueue.main.async {
  170. self.captchaCD -= 1
  171. self.timerLabel.text = "\(self.captchaCD)秒"
  172. if self.captchaCD <= 0 {
  173. self.setCaptcha(enable: true)
  174. }
  175. }
  176. }
  177. }
  178. }
  179. }
  180. /// 发送短信验证码的方法
  181. private func sendCaptcha() {
  182. /// 数据异常
  183. guard let accountManager = MTP2BusinessCore.shared.accountManager else { return }
  184. /// 手机号码
  185. let mobile = mobileTextField.text ?? ""
  186. accountManager.requestSendMobileCaptchaCode(mobile, areaCode, callback: { (isCompleted, errorInfo, showRefer, vcode) in
  187. DispatchQueue.main.async {
  188. if !isCompleted {
  189. self.showHintController(title: "提示", message: errorInfo?.retMsg ?? "发送失败,请稍后重试")
  190. return
  191. } else {
  192. /// 是否显示推荐码
  193. self.isShowRefer = showRefer ?? 0
  194. }
  195. }
  196. })
  197. self.setCaptcha(enable: false)
  198. }
  199. /// 三方登录注册的方法
  200. ///
  201. /// - Parameter memareacode: 所属机构代码
  202. private func authSignup(_ memareacode: String?) {
  203. /// 数据异常
  204. guard let accountManager = MTP2BusinessCore.shared.accountManager else { return }
  205. /// pleaseEnterCheckCode
  206. guard let captcha = self.captchaTextField.text else {
  207. WHToast.showError(withMessage: "pleaseEnterCheckCode", duration: 1.0, finishHandler: {})
  208. return
  209. }
  210. /// 缺少必要数据,请稍后重试
  211. guard let openID = self.openID,
  212. let authType = self.authType,
  213. let mobile = mobileTextField.text else {
  214. WHToast.showError(withMessage: "缺少必要数据,请稍后重试", duration: 1.0, finishHandler: {})
  215. return
  216. }
  217. /// 三方登录注册
  218. accountManager.authSignup(mobile: mobile, captcha: captcha, authType: authType, openID: openID, memareacode: memareacode, verifyType: nil) {
  219. isCompleted, resultDic in
  220. if let resultDic = resultDic, let success = resultDic["success"] as? String, let msg = resultDic["msg"] as? String {
  221. if success == "false" {
  222. /// 注册返回false,可能为需要补机构代码,也可以是注册失败
  223. let array = msg.split(separator: "^")
  224. if array.count > 1 && array[1] == "2" {
  225. /// 需要补充所属机构 “请输入会员和机构代码”
  226. DispatchQueue.main.async {
  227. let alertController = UIAlertController(title: "提示", message: "请输入会员或机构代码", preferredStyle: .alert)
  228. alertController.addTextField {
  229. textField in
  230. textField.keyboardType = UIKeyboardType.numberPad
  231. }
  232. alertController.addAction(UIAlertAction(title: "提交", style: .destructive) {
  233. action in
  234. guard let textField = alertController.textFields?[0], let value = textField.text else {
  235. WHToast.showError(withMessage: "请输入会员或机构代码", duration: 1.0, finishHandler: {})
  236. return
  237. }
  238. self.authSignup(value)
  239. })
  240. alertController.addAction(UIAlertAction(title: "cancle", style: .cancel) {
  241. action in
  242. DispatchQueue.main.sync {
  243. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  244. }
  245. })
  246. self.present(alertController, animated: true, completion: nil)
  247. }
  248. } else {
  249. DispatchQueue.main.sync {
  250. // 正常失败
  251. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  252. self.showHintController(title: "提示", message: "\(msg.replacingOccurrences(of: "^1", with: ""))")
  253. }
  254. }
  255. return
  256. } else {
  257. /// 注册成功,并已登录成功,进行连接恢复和Token校验
  258. /// 构建LoginRsp
  259. do {
  260. guard let range = msg.range(of: "?response=", options: .backwards, range: nil, locale: nil), let probufDataString = msg.suffix(msg.lengthOfBytes(using: .utf8) - range.upperBound.utf16Offset(in: "")).removingPercentEncoding, let data = Data(base64Encoded: String(probufDataString), options: Data.Base64DecodingOptions.init(rawValue: 0)) else {
  261. DispatchQueue.main.sync {
  262. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  263. self.showHintController(title: "提示", message: "注册失败,请稍后重试")
  264. }
  265. return
  266. }
  267. let authLoginRsp = try AuthLoginRsp(serializedData: data)
  268. if authLoginRsp.retCode == 0 {
  269. let loginRsp = LoginRsp.with {
  270. $0.loginID = authLoginRsp.loginID
  271. $0.loginUserType = authLoginRsp.loginUserType
  272. $0.accountStatus = authLoginRsp.accountStatus
  273. $0.userID = authLoginRsp.userID
  274. $0.memberUserID = authLoginRsp.memberUserID
  275. $0.accountIds = authLoginRsp.accountIds
  276. $0.token = authLoginRsp.token
  277. $0.systemTime = authLoginRsp.systemTime
  278. $0.hasUpdatedPwd_p = authLoginRsp.hasUpdatedPwd_p
  279. }
  280. accountManager.thenLogined(loginRsp: loginRsp)
  281. /// 连接交易链路,并进行CheckToken
  282. accountManager.recoverLogin(loginRsp: loginRsp) {
  283. isCompleted in
  284. if !isCompleted {
  285. DispatchQueue.main.sync {
  286. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  287. self.showHintController(title: "提示", message: "loginFailPleaseTryAgainLater")
  288. }
  289. return
  290. }
  291. /// 调用登录后的处理方法
  292. self.loginedToDo()
  293. }
  294. } else {
  295. DispatchQueue.main.sync {
  296. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  297. self.showHintController(title: "提示", message: ErrorUtils.getDesc(errorCode: Int(authLoginRsp.retCode)) ?? "loginFailPleaseTryAgainLater")
  298. }
  299. }
  300. } catch {
  301. DispatchQueue.main.sync {
  302. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  303. self.showHintController(title: "提示", message: "loginFailPleaseTryAgainLater")
  304. }
  305. }
  306. }
  307. }
  308. }
  309. }
  310. /// 手机号码登录的方法
  311. ///
  312. /// - Parameter memareacode: 所属机构代码
  313. private func mobileLogin(_ memareacode: String?) {
  314. /// 输入不合规
  315. guard checkInputValue(),
  316. let accountManager = MTP2BusinessCore.shared.accountManager else { return }
  317. /// 手机号码
  318. let mobile = mobileTextField.text ?? ""
  319. /// 短信验证码
  320. let captcha = captchaTextField.text ?? ""
  321. /// 推荐码
  322. let referral = isShowRefer == 0 ? nil : (referralField.text ?? "")
  323. /// 开启Loading
  324. NVActivityIndicatorPresenter.sharedInstance.startAnimating(ActivityData(message: "请稍等……", type: .ballClipRotate, color: UIColorFromHex(rgbValue: 0xA8B6CC)), NVActivityIndicatorView.DEFAULT_FADE_IN_ANIMATION)
  325. /// 手机号码登录
  326. accountManager.mobileLogin(mobile: areaCode+mobile, referral, captcha: captcha, memareacode: memareacode) {
  327. isCompleted, resultDic in
  328. if let resultDic = resultDic, let success = resultDic["success"] as? String, let msg = (resultDic["msg"] as? String)?.removingPercentEncoding {
  329. if success == "false" {
  330. /// 注册返回false,可能为需要补机构代码,也可以是注册失败
  331. let array = msg.split(separator: "^")
  332. if array.count > 1 && array[1] == "2" {
  333. /// 需要补充所属机构 “请输入会员和机构代码”
  334. DispatchQueue.main.async {
  335. let alertController = UIAlertController(title: "提示", message: "请输入会员或机构代码", preferredStyle: .alert)
  336. alertController.addTextField {
  337. textField in
  338. textField.keyboardType = UIKeyboardType.numberPad
  339. }
  340. alertController.addAction(UIAlertAction(title: "提交", style: .destructive) {
  341. action in
  342. guard let textField = alertController.textFields?[0], let value = textField.text else {
  343. self.showHintController(title: "提示", message: "请输入会员或机构代码")
  344. return
  345. }
  346. self.mobileLogin(value)
  347. })
  348. alertController.addAction(UIAlertAction(title: "cancle", style: .cancel) {
  349. action in
  350. DispatchQueue.main.sync {
  351. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  352. }
  353. })
  354. self.present(alertController, animated: true, completion: nil)
  355. }
  356. } else {
  357. DispatchQueue.main.sync {
  358. /// 正常失败
  359. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  360. self.showHintController(title: "提示", message: "\(msg.replacingOccurrences(of: "^1", with: ""))")
  361. }
  362. }
  363. return
  364. } else {
  365. /// 注册成功,并已登录成功,进行连接恢复和Token校验
  366. /// 构建LoginRsp
  367. do {
  368. guard let data = Data(base64Encoded: msg, options: Data.Base64DecodingOptions.init(rawValue: 0)) else {
  369. DispatchQueue.main.sync {
  370. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  371. self.showHintController(title: "提示", message: "loginFailPleaseTryAgainLater")
  372. }
  373. return
  374. }
  375. /// 手机号码登录返回的是LoginRsp
  376. let loginRsp = try LoginRsp(serializedData: data)
  377. if loginRsp.retCode == 0 {
  378. accountManager.thenLogined(loginRsp: loginRsp)
  379. /// 连接交易链路,并进行CheckToken
  380. accountManager.recoverLogin(loginRsp: loginRsp) {
  381. isCompleted in
  382. if isCompleted == false {
  383. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  384. self.showHintController(title: "提示", message: "loginFailPleaseTryAgainLater")
  385. return
  386. }
  387. /// 记录本次登录的手机号码
  388. UserDefaultsUtils.setLoginPhone(mobile)
  389. /// 调用登录后的处理方法
  390. self.loginedToDo()
  391. }
  392. } else {
  393. DispatchQueue.main.sync {
  394. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  395. self.showHintController(title: "提示", message: ErrorUtils.getDesc(errorCode: Int(loginRsp.retCode)) ?? "loginFailPleaseTryAgainLater")
  396. }
  397. }
  398. } catch {
  399. DispatchQueue.main.sync {
  400. NVActivityIndicatorPresenter.sharedInstance.stopAnimating(NVActivityIndicatorView.DEFAULT_FADE_OUT_ANIMATION)
  401. self.showHintController(title: "提示", message: "loginFailPleaseTryAgainLater")
  402. }
  403. }
  404. }
  405. }
  406. }
  407. }
  408. /// 登录成功后调用的方法
  409. func loginedToDo() {
  410. /// FIXME: - 实属无奈才把ui的内容放到这里,后期要处理
  411. let appDelegate = UIApplication.shared.delegate as! AppDelegate
  412. /// 异常
  413. guard let accountManager = MTP2BusinessCore.shared.accountManager else { return }
  414. /// 用户与商品信息统一查询
  415. accountManager.queryAccountAndGoods(callback: {
  416. (isCompleted, error) in
  417. if isCompleted {
  418. /// TODO: 查询是否需要修改密码
  419. accountManager.configQueryPostting{ (isSuccess, errorInfo) in
  420. DispatchQueue.main.async {
  421. /// 记录本地用户信息
  422. try? DatabaseHelper.addUser(userID: MTP2BusinessCore.shared.getUserID(), loginID: String(MTP2BusinessCore.shared.getLoginID()), loginDate: Date(), type: Int16(appDelegate.currentAuthType))
  423. /// 进入主界面
  424. let storyboard = UIStoryboard(name: "Main", bundle: nil)
  425. if let mainTabBarController: UITabBarController = storyboard.instantiateViewController(withIdentifier: "MainTabBarController") as? UITabBarController {
  426. appDelegate.window?.rootViewController = mainTabBarController
  427. }
  428. }
  429. }
  430. } else {
  431. DispatchQueue.main.async {
  432. if appDelegate.window?.rootViewController?.isKind(of: LoginViewController.self) ?? false,
  433. let loginViewController = (appDelegate.window?.rootViewController as? BaseNavigationController)?.topViewController as? LoginViewController {
  434. loginViewController.showHintController(title: "提示", message: "注册失败,请稍后重试")
  435. }
  436. }
  437. }
  438. })
  439. }
  440. /// 合规性校验
  441. private func checkInputValue() -> Bool {
  442. /// 手机号码
  443. guard let mobile = mobileTextField.text, mobile != "" else {
  444. WHToast.showError(withMessage: "请输入手机号码", duration: 1.5, finishHandler: {})
  445. return false
  446. }
  447. /// 短信验证码
  448. guard let captcha = self.captchaTextField.text, captcha != "" else {
  449. WHToast.showError(withMessage: "请输入验证码", duration: 1.5, finishHandler: {})
  450. return false
  451. }
  452. /// 需要显示推荐码
  453. if isShowRefer != 0 {
  454. /// 短信验证码
  455. guard let referral = self.referralField.text, referral != "" else {
  456. WHToast.showError(withMessage: "请输入推荐码", duration: 1.5, finishHandler: {})
  457. return false
  458. }
  459. }
  460. return true
  461. }
  462. }