RAOfflineHandler.m 36 KB

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