RAOfflineHandler.m 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. //
  2. // RAOfflineHandler.m
  3. // Apex And Drivers
  4. //
  5. // Created by Jack on 2018/10/22.
  6. // Copyright © 2018年 USAI. All rights reserved.
  7. //
  8. #import "RAOfflineHandler.h"
  9. #import "NetworkUtils.h"
  10. #import "RADetailBaseModel.h"
  11. #import "RAHomeOrderModel.h"
  12. #import "RADetailActionModel.h"
  13. #import "AppDelegate.h"
  14. #import "RAEditImageBaseModel.h"
  15. #import "RAArchiver.h"
  16. #define Lock() dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER)
  17. #define Unlock() dispatch_semaphore_signal(_lock)
  18. static dispatch_semaphore_t _lock;
  19. @interface RAOfflineHandler () {
  20. NSString *_offlineDir;
  21. NSString *_offlineTmp;
  22. NSString *_offlineUploadDir;
  23. NSString *_zipPassword;
  24. }
  25. @property (nonatomic,strong) NSMutableDictionary *detailCache;///< 缓存查看过的Detai
  26. @property (nonatomic,strong,readonly) NSString *offlineDir; ///< 缓存文件夹
  27. @property (nonatomic,strong,readonly) NSString *offlineTmp; ///< 临时保存解压文件的文件夹
  28. @property (nonatomic,strong,readonly) NSString *offlineUploadDir; ///<上传文件目录
  29. @property (nonatomic,copy,readonly) NSString *zipPassword; ///<压缩/解压 密码
  30. @end
  31. @implementation RAOfflineHandler
  32. #pragma mark - Handler
  33. + (instancetype)defaultHandler {
  34. static RAOfflineHandler *handler;
  35. static dispatch_once_t token;
  36. dispatch_once(&token, ^{
  37. handler = [[RAOfflineHandler alloc] init];
  38. _lock = dispatch_semaphore_create(1);
  39. });
  40. return handler;
  41. }
  42. - (void)prepareParams:(NSMutableDictionary* )params {
  43. NSAssert(params != nil, @"params can't be nil");
  44. NSString *user = RASingleton.sharedInstance.encryptUser;
  45. NSString *password = RASingleton.sharedInstance.encryptPassword;
  46. if (user.length && password.length) {
  47. [params setObject:user forKey:@"name"];
  48. [params setObject:password forKey:@"password"];
  49. }
  50. [params setObject:@"iOS" forKey:@"platform"];
  51. NSDictionary* infoDict =[[NSBundle mainBundle] infoDictionary];
  52. NSString* short_version =[infoDict objectForKey:@"CFBundleShortVersionString"];
  53. [params setValue:short_version forKey:@"app_short_ver"];
  54. NSString *localeLanguageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode];
  55. [params setValue:localeLanguageCode forKey:@"language"];
  56. NSString *time = [self currentDate];
  57. [params setObject:time forKey:@"date"];
  58. [params setObject:@(1) forKey:@"offline"];
  59. #if TARGET_IPHONE_SIMULATOR//模拟器
  60. [params setValue:@"simulator_uuid" forKey:@"deviceid"];
  61. #elif TARGET_OS_IPHONE//真机
  62. UIDevice * dev = [UIDevice currentDevice];
  63. NSUUID* uuid =dev.identifierForVendor;
  64. [params setValue:uuid.UUIDString forKey:@"deviceid"];
  65. #endif
  66. }
  67. - (NSString *)offlineDir {
  68. if (!_offlineDir) {
  69. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
  70. _offlineDir = [cachePath stringByAppendingPathComponent:@"offline"];
  71. NSFileManager *fm = [NSFileManager defaultManager];
  72. BOOL isDir = YES;
  73. if ([fm fileExistsAtPath:_offlineDir isDirectory:&isDir] && isDir) {
  74. } else {
  75. [fm createDirectoryAtPath:_offlineDir withIntermediateDirectories:NO attributes:nil error:nil];
  76. }
  77. }
  78. return _offlineDir;
  79. }
  80. - (NSString *)offlineTmp {
  81. if (!_offlineTmp) {
  82. _offlineTmp = [self.offlineDir stringByAppendingPathComponent:@"tmp"];
  83. NSFileManager *fm = [NSFileManager defaultManager];
  84. BOOL isDir = YES;
  85. if ([fm fileExistsAtPath:_offlineTmp isDirectory:&isDir] && isDir) {
  86. } else {
  87. [fm createDirectoryAtPath:_offlineTmp withIntermediateDirectories:YES attributes:nil error:nil];
  88. }
  89. }
  90. return _offlineTmp;
  91. }
  92. - (NSString *)offlineUploadDir {
  93. if (!_offlineUploadDir) {
  94. NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
  95. _offlineUploadDir = [docDir stringByAppendingPathComponent:@"upload"];
  96. NSFileManager *fm = [NSFileManager defaultManager];
  97. BOOL isDir = YES;
  98. if ([fm fileExistsAtPath:_offlineUploadDir isDirectory:&isDir] && isDir) {
  99. } else {
  100. [fm createDirectoryAtPath:_offlineUploadDir withIntermediateDirectories:YES attributes:nil error:nil];
  101. }
  102. }
  103. return _offlineUploadDir;
  104. }
  105. - (NSString *)zipPassword {
  106. if (!_zipPassword) {
  107. _zipPassword = @"usai";
  108. }
  109. return _zipPassword;
  110. }
  111. #pragma mark - Download
  112. /**
  113. * @brief 向服务器请求离线数据
  114. */
  115. - (void)downloadOfflineData {
  116. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  117. NSMutableDictionary *params = [NSMutableDictionary dictionary];
  118. [self prepareParams:params];
  119. [NetworkUtils downloadFileOffset:0 Param:params from:@"http://192.168.0.130/fake_offline.zip" method:@"POST" toPath:self.offlineDir progressHandler:^(NSURLSessionTask *task, double progress) {
  120. } completionHandler:^(NSMutableDictionary *result) {
  121. int rs = [[result objectForKey:@"result"] intValue];
  122. NSString *path = [result objectForKey:@"path"];
  123. if (rs == RESULT_TRUE) {
  124. [self handleDownloadFile:path];
  125. }
  126. }];
  127. // NSString *path = [self.offlineDir stringByAppendingPathComponent:@"download.zip"];
  128. // [self handleDownloadFile:path];
  129. //
  130. });
  131. }
  132. /**
  133. * @brief 加载沙盒中的数据
  134. * @param path 沙盒数据路径
  135. */
  136. - (NSDictionary *)_loadCacheData:(NSString *)path {
  137. if (path == nil || path.length == 0) {
  138. return nil;
  139. }
  140. NSData *data = [NSData dataWithContentsOfFile:path];
  141. if (data) {
  142. NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
  143. return dic;
  144. }
  145. return nil;
  146. }
  147. - (void)_moveFile:(NSString *)filePath toFolder:(NSString *)folder {
  148. NSFileManager *fm = [NSFileManager defaultManager];
  149. BOOL isDir = NO;
  150. if ([fm fileExistsAtPath:filePath isDirectory:&isDir] && !isDir) {
  151. BOOL fIsDir = NO;
  152. if ([fm fileExistsAtPath:folder isDirectory:&fIsDir] && fIsDir) {
  153. } else {
  154. NSError *err = nil;
  155. [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:&err];
  156. if (err) {
  157. NSLog(@"create folder error: %@",err);
  158. return;
  159. }
  160. }
  161. NSString *to = [folder stringByAppendingPathComponent:filePath.lastPathComponent];
  162. NSError *err = nil;
  163. [fm moveItemAtPath:filePath toPath:to error:&err];
  164. if (err) {
  165. NSLog(@"move file error: %@",err);
  166. }
  167. }
  168. }
  169. - (void)_moveFolder:(NSString *)srcfolder toFolder:(NSString *)destFolder {
  170. NSFileManager *fm = [NSFileManager defaultManager];
  171. BOOL isDir = NO;
  172. if ([fm fileExistsAtPath:srcfolder isDirectory:&isDir] && isDir) {
  173. NSString *dest = [destFolder stringByAppendingPathComponent:srcfolder.lastPathComponent];
  174. isDir = NO;
  175. if ([fm fileExistsAtPath:dest isDirectory:&isDir] && isDir) {
  176. } else {
  177. NSError *err = nil;
  178. [fm createDirectoryAtPath:dest withIntermediateDirectories:YES attributes:nil error:&err];
  179. if (err) {
  180. NSLog(@"create folder error: %@",err);
  181. return;
  182. }
  183. }
  184. NSArray<NSString *> *contents = [fm contentsOfDirectoryAtPath:srcfolder error:nil];
  185. if (contents.count > 0) {
  186. [contents enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  187. NSString *path = [srcfolder stringByAppendingPathComponent:obj];
  188. BOOL fileIsDir = NO;
  189. if ([fm fileExistsAtPath:path isDirectory:&fileIsDir]) {
  190. if (fileIsDir) {
  191. [self _moveFolder:path toFolder:dest];
  192. } else {
  193. [self _moveFile:path toFolder:dest];
  194. }
  195. }
  196. }];
  197. }
  198. }
  199. }
  200. /**
  201. * @brief 处理下载的压缩文件
  202. * @param path 压缩文件路径
  203. */
  204. - (void)handleDownloadFile:(NSString *)path {
  205. Lock();
  206. // 解压
  207. BOOL zipRes = [self _unzipOfflineZip:path toDir:self.offlineTmp];
  208. if (zipRes) {
  209. NSFileManager *fm = [NSFileManager defaultManager];
  210. // 删除旧数据,除了tmp
  211. NSArray<NSString *> *items = [fm contentsOfDirectoryAtPath:self.offlineDir error:nil];
  212. [items enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  213. NSString *path = [self.offlineDir stringByAppendingPathComponent:obj];
  214. if (![path isEqualToString:self.offlineTmp] && ![path.lastPathComponent isEqualToString:@"finish"] && ![path isEqualToString:self.offlineUploadDir]) {
  215. [fm removeItemAtPath:path error:nil];
  216. }
  217. }];
  218. // 将tmp中解压数据移出来
  219. items = [fm contentsOfDirectoryAtPath:self.offlineTmp error:nil];
  220. [items enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  221. NSString *src = [self.offlineTmp stringByAppendingPathComponent:obj];
  222. BOOL isDir = NO;
  223. if ([fm fileExistsAtPath:src isDirectory:&isDir]) {
  224. if (isDir) {
  225. [self _moveFolder:src toFolder:self.offlineDir];
  226. } else {
  227. [self _moveFile:src toFolder:self.offlineDir];
  228. }
  229. }
  230. }];
  231. // 重置缓存的action
  232. [self _resetFinish];
  233. }
  234. Unlock();
  235. }
  236. /**
  237. * @brief 重置离线完成的Action,将finished order对应的action的剔除
  238. */
  239. - (void)_resetFinish {
  240. NSDictionary *homeJson = [self _requestOfflineHome];
  241. NSArray<NSDictionary *> *sections = [homeJson objectForKey:@"sections"];
  242. NSMutableDictionary *finishDic = [NSMutableDictionary dictionary];
  243. NSDictionary *finishDicTmp = [self _finishedActions];
  244. if (finishDicTmp.count > 0) {
  245. [sections enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  246. NSArray<NSDictionary *> *orders = [obj objectForKey:@"orders"];
  247. [orders enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull order, NSUInteger idx, BOOL * _Nonnull stop) {
  248. NSString *orderId = [order objectForKey:@"orderID"];
  249. NSNumber *finish = [finishDicTmp objectForKey:orderId];
  250. if (finish) {
  251. [finishDic setObject:finish forKey:orderId];
  252. }
  253. }];
  254. }];
  255. }
  256. NSString *finishPath = [self.offlineDir stringByAppendingPathComponent:@"finish"];
  257. [finishDic writeToFile:finishPath atomically:NO];
  258. }
  259. #pragma mark - Request Data
  260. /**
  261. * @brief 加载离线首页数据
  262. */
  263. - (NSDictionary *)_requestOfflineHome {
  264. NSString *homeJsonPath = [self.offlineDir stringByAppendingPathComponent:@"home.json"];
  265. NSDictionary *result = [self _loadCacheData:homeJsonPath];
  266. return result;
  267. }
  268. /**
  269. * @brief 加载离线首页数据
  270. */
  271. - (NSDictionary *)requestOfflineHome {
  272. Lock();
  273. NSDictionary *result = [self _requestOfflineHome];
  274. Unlock();
  275. return result;
  276. }
  277. /**
  278. * @brief 加载离线Detail
  279. * @param orderId 订单号
  280. * @param type 订单类型,包括 new order 和 processing order
  281. */
  282. - (NSDictionary *)requestOfflineDetailForOrder:(NSString *)orderId withOrderType:(NSInteger)type {
  283. Lock();
  284. NSString *detailJsonPath = [self.offlineDir stringByAppendingPathComponent:[NSString stringWithFormat:@"detail/%@_%ld",orderId,(long)type]];
  285. NSMutableDictionary *detailJson = [[self _loadCacheData:detailJsonPath] mutableCopy];
  286. NSNumber *finish = [self _lastActionIndexForOrder:orderId];
  287. detailJson = [self filtrateActionFromDetail:detailJson withFinishActions:finish];
  288. Unlock();
  289. return detailJson;
  290. }
  291. /**
  292. * @brief 记载离线Edit Order
  293. * @param orderId 订单号
  294. * @param actionIndex 操作标记
  295. */
  296. - (NSDictionary *)requestOfflineEditOrder:(NSString *)orderId withAction:(NSInteger)actionIndex {
  297. Lock();
  298. NSString *editJsonPath = [self.offlineDir stringByAppendingPathComponent:[NSString stringWithFormat:@"edit/%@_%ld",orderId,(long)actionIndex]];
  299. NSDictionary *editJson = [self _loadCacheData:editJsonPath];
  300. Unlock();
  301. return editJson;
  302. }
  303. #pragma mark - Utils
  304. /**
  305. * @brief 解压离线下载的压缩包
  306. * @return 解压是否成功
  307. */
  308. - (BOOL)_unzipOfflineZip:(NSString *)path toDir:(NSString *)to {
  309. return [RAArchiver ra_unzipOfflineZip:path toDir:to withPassword:self.zipPassword];
  310. }
  311. /**
  312. * @brief 压缩文件夹
  313. * @param dir 被压缩文件夹
  314. * @return 压缩文件路径
  315. */
  316. - (NSString *)_zipDir:(NSString *)dir {
  317. NSString *zipFile = [dir stringByAppendingPathExtension:@"zip"];
  318. if ([RAArchiver ra_zipFile:dir withPassword:self.zipPassword]) {
  319. return zipFile;
  320. }
  321. return nil;
  322. }
  323. /**
  324. * @brief 在detail中过滤订单已经做过的操作
  325. * @param detail order detail界面数据
  326. * @param finish order最后完成的一个action索引
  327. */
  328. - (NSMutableDictionary *)filtrateActionFromDetail:(NSMutableDictionary *)detail withFinishActions:(NSNumber *)finish {
  329. if (finish == nil) {
  330. return detail;
  331. }
  332. int result = [[detail objectForKey:@"result"] intValue];
  333. if (result != RESULT_TRUE) {
  334. return detail;
  335. }
  336. // 一层层解开,获取到目标值后作出修改,然后反向一层层的套回去
  337. NSMutableArray<NSDictionary *> *sections = [[detail objectForKey:@"sections"] mutableCopy];
  338. NSMutableIndexSet *rmSectionArr = [NSMutableIndexSet indexSet];
  339. [sections enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  340. NSMutableDictionary *section = [obj mutableCopy];
  341. NSMutableArray<NSDictionary *> *values = [[section objectForKey:@"values"] mutableCopy];
  342. NSMutableIndexSet *rmValueArr = [NSMutableIndexSet indexSet];
  343. [values enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  344. int type = [[obj objectForKey:@"type"] intValue];
  345. if (type == RAOrderDetailValueTypeAction) {
  346. NSMutableDictionary *mObj = [obj mutableCopy];
  347. NSMutableArray<NSDictionary *> *actions = [[mObj objectForKey:@"actions"] mutableCopy];
  348. NSMutableArray *rmArr = [NSMutableArray array];
  349. [actions enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  350. NSInteger index = [[obj objectForKey:@"index"] integerValue];
  351. if ([finish integerValue] >= index) {
  352. [rmArr addObject:obj];
  353. }
  354. }];
  355. [actions removeObjectsInArray:rmArr];
  356. if (actions.count == 0) {
  357. [rmValueArr addIndex:idx];
  358. }
  359. [mObj setObject:actions forKey:@"actions"];
  360. [values replaceObjectAtIndex:idx withObject:mObj];
  361. }
  362. }];
  363. if (rmValueArr.count > 0) {
  364. [values removeObjectsAtIndexes:rmValueArr];
  365. }
  366. if (values.count == 0) {
  367. [rmSectionArr addIndex:idx];
  368. }
  369. [section setObject:values forKey:@"values"];
  370. [sections replaceObjectAtIndex:idx withObject:section];
  371. }];
  372. if (rmSectionArr.count > 0) {
  373. [sections removeObjectsAtIndexes:rmSectionArr];
  374. }
  375. [detail setObject:sections forKey:@"sections"];
  376. return detail;
  377. }
  378. /**
  379. * @brief 判断Order Action是否是最后一步
  380. * @param orderId 订单号
  381. * @return YES/NO
  382. */
  383. - (BOOL)isLastActionForOrder:(NSString *)orderId index:(NSInteger)actionIndex {
  384. if (!orderId) {
  385. return NO;
  386. }
  387. NSDictionary *detail = [self requestOfflineDetailForOrder:orderId withOrderType:RAOrderStatusProcessing];
  388. int result = [[detail objectForKey:@"result"] intValue];
  389. if (result != RESULT_TRUE) {
  390. return NO;
  391. }
  392. __block BOOL isLast = NO;
  393. NSMutableArray<NSDictionary *> *sections = [[detail objectForKey:@"sections"] mutableCopy];
  394. [sections enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  395. NSMutableDictionary *section = [obj mutableCopy];
  396. NSMutableArray<NSDictionary *> *values = [[section objectForKey:@"values"] mutableCopy];
  397. __block BOOL findAction = NO;
  398. [values enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  399. int type = [[obj objectForKey:@"type"] intValue];
  400. if (type == RAOrderDetailValueTypeAction) {
  401. findAction = YES;
  402. NSMutableDictionary *mObj = [obj mutableCopy];
  403. NSMutableArray<NSDictionary *> *actions = [[mObj objectForKey:@"actions"] mutableCopy];
  404. __block int maxActionIndex = 0;
  405. [actions enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  406. int index = [[obj objectForKey:@"index"] intValue];
  407. maxActionIndex = MAX(maxActionIndex, index);
  408. }];
  409. if (actionIndex == maxActionIndex) {
  410. isLast = YES;
  411. }
  412. *stop = YES;
  413. }
  414. }];
  415. if (findAction) {
  416. *stop = YES;
  417. }
  418. }];
  419. return isLast;
  420. }
  421. /**
  422. * @brief 加载所有完成的订单Action
  423. */
  424. - (NSDictionary<NSString *, NSNumber *> *)_finishedActions {
  425. NSString *path = [self.offlineDir stringByAppendingPathComponent:@"finish"];
  426. NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:path];
  427. if (dic == nil) {
  428. dic = [NSDictionary dictionary];
  429. }
  430. return dic;
  431. }
  432. /**
  433. * @brief 最后一次修改Order的 Action 索引
  434. * @param orderId 订单号
  435. */
  436. - (NSNumber *)_lastActionIndexForOrder:(NSString *)orderId {
  437. NSDictionary *finishDic = [self _finishedActions];
  438. NSNumber *finish = [finishDic objectForKey:orderId];
  439. return finish;
  440. }
  441. /**
  442. * @brief 最后一次修改Order的 Action 索引
  443. * @param orderId 订单号
  444. */
  445. - (NSNumber *)lastActionIndexForOrder:(NSString *)orderId {
  446. Lock();
  447. NSDictionary *finishDic = [self _finishedActions];
  448. NSNumber *finish = [finishDic objectForKey:orderId];
  449. Unlock();
  450. return finish;
  451. }
  452. /**
  453. * @brief 删除文件
  454. * @param path 待删除文件路径
  455. */
  456. - (void)deleteFileAtPath:(NSString *)path {
  457. Lock();
  458. NSFileManager *fm = [NSFileManager defaultManager];
  459. if([fm fileExistsAtPath:path]) {
  460. [fm removeItemAtPath:path error:nil];
  461. }
  462. Unlock();
  463. }
  464. /**
  465. * @brief 将字典以Json格式写入文件
  466. * @param json 待写入字典数据
  467. * @param path 写入文件路径
  468. */
  469. - (void)_writeJson:(NSDictionary *)json toPath:(NSString *)path {
  470. NSData *data = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:nil];
  471. [data writeToFile:path atomically:NO];
  472. }
  473. /**
  474. * @brief 将字典以Json格式写入文件
  475. * @param json 待写入字典数据
  476. * @param path 写入文件路径
  477. */
  478. - (void)writeJson:(NSDictionary *)json toPath:(NSString *)path {
  479. Lock();
  480. [self _writeJson:json toPath:path];
  481. Unlock();
  482. }
  483. /**
  484. * @brief 当前时间字符串,格式 MM/DD/YYYY HH:mm
  485. */
  486. - (NSString *)currentDate {
  487. NSDate *date = [NSDate date];
  488. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  489. formatter.dateFormat = @"MM/dd/YYYY HH:mm";
  490. return [formatter stringFromDate:date];
  491. }
  492. - (NSString *)statusTitleForActionIndex:(NSInteger)actionIndex {
  493. Lock();
  494. NSString *path = [self.offlineDir stringByAppendingPathComponent:@"status_title.json"];
  495. NSDictionary *titleDic = [self _loadCacheData:path];
  496. Unlock();
  497. NSString *title = [titleDic objectForKey:[NSString stringWithFormat:@"%ld",(long)actionIndex]];
  498. if (!title) {
  499. title = @"";
  500. }
  501. return title;
  502. }
  503. - (NSString *)homeSectionTitleForStatus:(NSInteger)status {
  504. Lock();
  505. NSString *path = [self.offlineDir stringByAppendingPathComponent:@"home_section_title.json"];
  506. NSDictionary *titleDic = [self _loadCacheData:path];
  507. Unlock();
  508. NSString *title = [titleDic objectForKey:[NSString stringWithFormat:@"%ld",(long)status]];
  509. if (!title) {
  510. title = @"";
  511. }
  512. return title;
  513. }
  514. - (void)homeSection:(NSMutableDictionary *)section addOrder:(NSDictionary *)curOrder forActionIndex:(NSInteger)actionIdx {
  515. if (curOrder == nil) {
  516. return;
  517. }
  518. NSMutableArray<NSDictionary *> *orders = [[section objectForKey:@"orders"] mutableCopy];
  519. if (orders == nil) {
  520. orders = [NSMutableArray array];
  521. }
  522. // 修改order状态为Processing
  523. [curOrder setValue:@(RAOrderStatusProcessing) forKey:@"status"];
  524. [curOrder setValue:@(NO) forKey:@"backendFlag"];
  525. [curOrder setValue:[self statusTitleForActionIndex:actionIdx] forKey:@"title"];
  526. // 将当前order插入第一个位置
  527. [orders insertObject:curOrder atIndex:0];
  528. [section setObject:orders forKey:@"orders"];
  529. }
  530. #pragma mark - Update Data
  531. /**
  532. * @brief 更新离线缓存Home数据
  533. * @param homeJson 新的Home数据
  534. */
  535. - (void)updateHome:(NSDictionary *)homeJson {
  536. NSString *homeJsonPath = [self.offlineDir stringByAppendingPathComponent:@"home.json"];
  537. [self writeJson:homeJson toPath:homeJsonPath];
  538. }
  539. /**
  540. * @brief 更新order最后一次操作标记
  541. * @param actionIdx 操作索引
  542. * @param orderId 订单号
  543. */
  544. - (void)updateLastAction:(NSInteger)actionIdx forOrder:(NSString *)orderId {
  545. if (!orderId) {
  546. return;
  547. }
  548. Lock();
  549. NSMutableDictionary<NSString *, NSNumber *> *finishDic = [[self _finishedActions] mutableCopy];
  550. Unlock();
  551. [finishDic setObject:@(actionIdx) forKey:orderId];
  552. NSString *path = [self.offlineDir stringByAppendingPathComponent:@"finish"];
  553. [finishDic writeToFile:path atomically:NO];
  554. }
  555. /**
  556. * @brief 删除order操作对应的json文件
  557. * @param orderId 订单号
  558. * @param actionIdx 操作索引
  559. */
  560. - (void)deleteEditJsonFileForOrder:(NSString *)orderId withActionIndex:(NSInteger)actionIdx {
  561. NSString *editJsonPath = [self.offlineDir stringByAppendingPathComponent:[NSString stringWithFormat:@"edit/%@_%ld",orderId,(long)actionIdx]];
  562. [self deleteFileAtPath:editJsonPath];
  563. }
  564. /**
  565. * @brief 离线提交Detail Remote Action
  566. * @param orderId 订单号
  567. * @param type 订单类型 RAOrderStatus, New Order / Processing Order
  568. * @param action 操作类型 RADetailActionSubType, Reject / Accept
  569. * @param actionIdx 操作索引
  570. * @param actionName 操作名称
  571. * @param url 操作提交目标地址
  572. * @param params 操作提交的参数
  573. * @return 成功/失败
  574. */
  575. - (NSDictionary *)reportOrder:(NSString *)orderId type:(NSInteger)type actionType:(NSInteger)action actionIndex:(NSInteger)actionIdx actionName:(NSString *)actionName withURL:(NSString *)url params:(NSDictionary *)params {
  576. // 组织参数
  577. NSString *time = [self currentDate];
  578. NSMutableDictionary *mParams = [params mutableCopy];
  579. [self prepareParams:mParams];
  580. NSMutableDictionary *task = [@{
  581. @"order" : orderId,
  582. @"action" : actionName,
  583. @"name" : actionName,
  584. @"time" : time,
  585. @"url" : url,
  586. @"noFile" : @YES,
  587. @"params" : mParams
  588. } mutableCopy];
  589. dispatch_async(dispatch_get_main_queue(), ^{
  590. // @"file" : photoPath,
  591. // 提交参数给UploadManager
  592. AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
  593. [appDelegate.uploadManager addTask:task];
  594. });
  595. // New Order
  596. if (type == RAOrderStatusNew) {
  597. // 加载Home
  598. NSMutableDictionary *home = [[self requestOfflineHome] mutableCopy];
  599. if (home != nil) {
  600. // 遍历order,查找当前order,并修改状态/移除
  601. // Accept 移动order到processing
  602. // Reject 删除Order及对应的detail、edit
  603. NSMutableArray<NSDictionary *> *sections = [[home objectForKey:@"sections"] mutableCopy];
  604. __block NSDictionary *curOrder = nil; // 当前order
  605. __block NSDictionary *rmSection = nil; // 若section下没有order,则移除section
  606. if (sections) {
  607. // 查找New Order
  608. for (int i = 0; i < sections.count; i++) {
  609. NSDictionary *section = [sections objectAtIndex:i];
  610. RAOrderStatus type = [[section objectForKey:@"type"] intValue];
  611. if (type == RAOrderStatusNew) {
  612. // 查找当前order
  613. NSMutableArray<NSDictionary *> *orders = [[section objectForKey:@"orders"] mutableCopy];
  614. [orders enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  615. NSString *orderID = [obj objectForKey:@"orderID"];
  616. if ([orderId isEqualToString:orderID]) {
  617. curOrder = obj;
  618. *stop = YES;
  619. }
  620. }];
  621. // 找到order并移除
  622. if (curOrder) {
  623. [orders removeObject:curOrder];
  624. // 移除过后如果orders为空,则移除section
  625. if (orders.count == 0) {
  626. rmSection = section;
  627. }
  628. else {
  629. // orders不为空,重新修改
  630. rmSection = nil;
  631. NSMutableDictionary *mSec = [section mutableCopy];
  632. [mSec setObject:orders forKey:@"orders"];
  633. [sections replaceObjectAtIndex:i withObject:mSec];
  634. }
  635. }
  636. // 删除Order 操作对应Edit文件
  637. [self deleteEditJsonFileForOrder:orderId withActionIndex:actionIdx];
  638. break;
  639. }
  640. }
  641. if (rmSection) {
  642. [sections removeObject:rmSection];
  643. }
  644. // 如果是Accept则将order添加到Processing Order
  645. if (action == RADetailActionSubTypeAccept) {
  646. BOOL findSection = NO;
  647. for (int i = 0; i < sections.count; i++) {
  648. NSMutableDictionary *section = [[sections objectAtIndex:i] mutableCopy];
  649. RAOrderStatus type = [[section objectForKey:@"type"] intValue];
  650. if (type == RAOrderStatusProcessing) {
  651. [self homeSection:section addOrder:curOrder forActionIndex:actionIdx];
  652. // 重新生成section
  653. [sections replaceObjectAtIndex:i withObject:section];
  654. findSection = YES;
  655. break;
  656. }
  657. }
  658. if (!findSection) {
  659. NSMutableDictionary *section = [@{
  660. @"type" : @(RAOrderStatusProcessing),
  661. @"totalCount" : @0,
  662. @"backendFlagCount" : @0,
  663. @"title" : [self homeSectionTitleForStatus:RAOrderStatusProcessing]
  664. } mutableCopy];
  665. [self homeSection:section addOrder:curOrder forActionIndex:actionIdx];
  666. [sections addObject:section];
  667. }
  668. }
  669. // 重新生成sections
  670. [home setObject:sections forKey:@"sections"];
  671. }
  672. // 更新Home文件
  673. [self updateHome:home];
  674. }
  675. }
  676. // 更新Action
  677. [self updateLastAction:actionIdx forOrder:orderId];
  678. return @{
  679. @"result" : @(RESULT_TRUE)
  680. };
  681. }
  682. /**
  683. * @brief 离线提交订单
  684. * @param orderId 订单号
  685. * @param actionId 操作ID,RADetailActionSubType
  686. * @param title 操作名称
  687. * @param idx 操作索引
  688. * @param params 订单填写的信息
  689. * @param photos 待上传的图片模型数组
  690. * @param dir 待上传的图片存储目录
  691. */
  692. - (NSDictionary *)updateOrder:(NSString *)orderId action:(NSInteger)actionId title:(NSString *)title index:(NSInteger)idx withParams:(NSDictionary *)params photos:(NSArray<RAEditImageBaseModel *> *)photos cacheDir:(NSString *)dir {
  693. NSFileManager *fm = [NSFileManager defaultManager];
  694. // 修改Params
  695. NSString *time = [self currentDate];
  696. NSMutableDictionary *mParams = [params mutableCopy];
  697. [self prepareParams:mParams];
  698. // // 将params写入目录
  699. // NSString *paramPath = [upDir stringByAppendingPathComponent:@"params.json"];
  700. // [self _writeJson:mParams toPath:paramPath];
  701. NSString *zipF = nil;
  702. if (photos.count > 0) {
  703. Lock();
  704. // 生成上传目录
  705. NSString *upDir = [self.offlineUploadDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%ld",orderId,(long)idx]];
  706. [fm createDirectoryAtPath:upDir withIntermediateDirectories:NO attributes:nil error:nil];
  707. // 生成图片目录
  708. NSString *imageDir = [upDir stringByAppendingPathComponent:@"images"];
  709. [fm createDirectoryAtPath:imageDir withIntermediateDirectories:NO attributes:nil error:nil];
  710. // 将图片移动到图片目录
  711. for (RAEditImageBaseModel *model in photos) {
  712. NSString *path = [dir stringByAppendingPathComponent:model.imageName];
  713. NSString *toPath = [imageDir stringByAppendingString:model.imageName];
  714. if ([fm fileExistsAtPath:path]) {
  715. NSError *err;
  716. BOOL move = [fm moveItemAtPath:path toPath:toPath error:&err];
  717. if (!move) {
  718. NSString *msg = NSLocalizedString(@"sorry", nil);
  719. if (err) {
  720. msg = err.localizedDescription;
  721. }
  722. Unlock();
  723. return @{
  724. @"result" : @(RESULT_FALSE),
  725. @"err_msg" : msg
  726. };
  727. }
  728. }
  729. }
  730. // 压缩目录
  731. NSString *zip = [self _zipDir:upDir];
  732. // 删除目录
  733. [fm removeItemAtPath:upDir error:nil];
  734. NSRange start = [zip rangeOfString:@"upload"];
  735. zipF = [zip substringFromIndex:start.location];
  736. Unlock();
  737. }
  738. // 创建Task
  739. NSMutableDictionary *task = [@{
  740. @"order" : orderId,
  741. @"action" : title,
  742. @"name" : title,
  743. @"time" : time,
  744. @"url" : URL_UPLOAD,
  745. @"params" : mParams
  746. } mutableCopy];
  747. if (zipF) {
  748. [task setObject:zipF forKey:@"file"];
  749. } else {
  750. [task setObject:@(YES) forKey:@"noFile"];
  751. }
  752. dispatch_async(dispatch_get_main_queue(), ^{
  753. // 提交Task
  754. AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
  755. [appDelegate.uploadManager addTask:task];
  756. });
  757. // 删除Edit文件
  758. [self deleteEditJsonFileForOrder:orderId withActionIndex:idx];
  759. // 判断是否完成order 所有操作
  760. BOOL finish = [self isLastActionForOrder:orderId index:idx];
  761. // 将order从Processing中删除
  762. // 加载Home
  763. NSMutableDictionary *home = [[self requestOfflineHome] mutableCopy];
  764. if (home) {
  765. NSMutableArray<NSDictionary *> *sections = [[home objectForKey:@"sections"] mutableCopy];
  766. if (sections) {
  767. __block NSDictionary *rmSection = nil; // 若section下没有order,则移除section
  768. for (int i = 0; i < sections.count; i++) {
  769. NSMutableDictionary *section = [[sections objectAtIndex:i] mutableCopy];
  770. RAOrderStatus type = [[section objectForKey:@"type"] intValue];
  771. if (type == RAOrderStatusProcessing) {
  772. NSMutableArray<NSDictionary *> *orders = [[section objectForKey:@"orders"] mutableCopy];
  773. __block NSDictionary *curOrder = nil; // 当前order
  774. [orders enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  775. NSString *orderID = [obj objectForKey:@"orderID"];
  776. if ([orderId isEqualToString:orderID]) {
  777. curOrder = obj;
  778. *stop = YES;
  779. }
  780. }];
  781. if (finish) {
  782. [orders removeObject:curOrder];
  783. if (orders.count > 0) {
  784. [section setObject:orders forKey:@"orders"];
  785. // 重新生成section
  786. [sections replaceObjectAtIndex:i withObject:section];
  787. } else {
  788. rmSection = [sections objectAtIndex:i];
  789. }
  790. } else {
  791. [curOrder setValue:@(NO) forKey:@"backendFlag"];
  792. [curOrder setValue:[self statusTitleForActionIndex:idx] forKey:@"title"];
  793. }
  794. break;
  795. }
  796. }
  797. if (rmSection) {
  798. [sections removeObject:rmSection];
  799. }
  800. // 重新生成sections
  801. [home setObject:sections forKey:@"sections"];
  802. }
  803. // 更新Home文件
  804. [self updateHome:home];
  805. }
  806. // 更新Action
  807. [self updateLastAction:idx forOrder:orderId];
  808. return @{
  809. @"result" : @(RESULT_TRUE)
  810. };
  811. }
  812. @end