最近公司開發(fā)一款新的電商類的app,所以自己整理了一套購(gòu)物車的業(yè)務(wù)邏輯,提供以后的復(fù)習(xí)和整理(這里討論的只是對(duì)購(gòu)物車信息進(jìn)行本地的存儲(chǔ),未考慮后臺(tái)存儲(chǔ)的情況)。
數(shù)據(jù)存儲(chǔ)框架(Core Data)
在這里使用的是蘋果提供的Core Data,這里不去討論如何使用和優(yōu)缺點(diǎn)問題,個(gè)人覺得對(duì)于移動(dòng)端來說,并不需要大型網(wǎng)站的高并發(fā),所以這點(diǎn)性能差別幾乎是沒有影響的。
對(duì)數(shù)據(jù)讀寫(MagicalRecord)
這里對(duì)數(shù)據(jù)庫(kù)的讀寫使用一個(gè)非常不錯(cuò)的輪子:MagicalRecord(當(dāng)然也可以自己寫一套讀寫數(shù)據(jù)庫(kù)的工具類出來,思路基本都是差不多的)。github地址:https://github.com/magicalpanda/MagicalRecord
購(gòu)物車的功能和實(shí)現(xiàn)思路
功能:
- 登錄/未登錄狀態(tài)都可以對(duì)購(gòu)物車中的商品進(jìn)行增刪改查;
- 用戶登錄后,需要把未登錄時(shí)選購(gòu)的商品轉(zhuǎn)移到該用戶名下,并清空未登錄時(shí)的購(gòu)物車信息;
- 更新了數(shù)據(jù)庫(kù)中的購(gòu)物車數(shù)據(jù),及時(shí)的通知到用戶。
思路
- 首先,我們得知道這個(gè)購(gòu)物車是屬于誰(shuí)的,每個(gè)登錄用戶都會(huì)有一個(gè)唯一的標(biāo)識(shí)符(ID),所以我們就把它作為購(gòu)物車的標(biāo)識(shí)符(ID),未登錄的情況我們可以定義一個(gè)固定的標(biāo)識(shí)符。
- 購(gòu)物車中的信息是由一家家商鋪和商鋪中被選中的商品構(gòu)成的集合,所以我們可以于商鋪?zhàn)鳛橘?gòu)物車中的一個(gè)個(gè)單元,商鋪的ID就是單元的ID。
- 我們對(duì)購(gòu)物車的轉(zhuǎn)移就是在用戶不同狀態(tài)之間的處理,對(duì)購(gòu)物車的更新實(shí)際就是對(duì)商鋪信息的更新,這樣的我們的業(yè)務(wù)邏輯就清晰了。
類的設(shè)計(jì)
涉及到的類
請(qǐng)不要在意項(xiàng)目中的類命名,我們看思路就行了 >v<
- 模型和類的介紹
// 信息存儲(chǔ)模型
ShopModel : 商鋪信息模型類
NewGoodsModel : 商品信息模型類
// 購(gòu)物車視圖模型
JYGoodsCartViewModel : 用戶所選商品信息(包括商品信息和購(gòu)買的數(shù)量等)
JYShopCartViewModel : 用戶在此商鋪中選購(gòu)的信息(包括商鋪信息、用戶所選的商品信息),即購(gòu)物車的組成單元。
// CoreData中的存儲(chǔ)模型
JYGoodsCart : 商品信息在CoreData中的存儲(chǔ)模型
JYShopCart : 數(shù)據(jù)庫(kù)中存儲(chǔ)的單元,即購(gòu)物車在Core Data對(duì)應(yīng)的模型類
// 工具類
JYShopCartHelper : 對(duì)數(shù)據(jù)庫(kù)進(jìn)行增刪改查的工具類,并兼具把CoreData模型轉(zhuǎn)換成購(gòu)物車的視圖模型
-
關(guān)系圖
關(guān)系圖
主要類的實(shí)現(xiàn)
ShopModel
- ShopModel.h
@interface ShopModel : NSObject
@property (nonatomic, copy) NSString *dmId; // 店鋪ID
@property (nonatomic, copy) NSString *businessName; // 店鋪名稱
@end
- ShopModel.m
因?yàn)樾枰獙?duì)模型進(jìn)行復(fù)制和歸檔解檔操作,所以需要實(shí)現(xiàn)以下方法:
@interface ShopModel () <NSCoding>
@end
@implementation ShopModel
#pragma mark - <NSCopying>
- (id)copyWithZone:(nullable NSZone *)zone {
ShopModel *model = [[[self class] alloc] init];
model.dmId = [self.dmId copy];
model.businessName = [self.businessName copy];
return model;
}
#pragma mark - encode
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.dmId forKey:@"dmId"];
[aCoder encodeObject:self.businessName forKey:@"businessName"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.dmId = [aDecoder decodeObjectForKey:@"dmId"];
self.businessName = [aDecoder decodeObjectForKey:@"businessName"];
}
return self;
}
@end
NewGoodsModel
(這里展示的只是一部分屬性)
- NewGoodsModel.h
@interface NewGoodsModel : NSObject
@property (nonatomic, copy) NSString *goodsId; // 商品ID
@property (nonatomic, copy) NSString *businessId; // 商鋪ID
@property (nonatomic, copy) NSString *goodsName; // 商品名稱
@property (nonatomic, assign) double price; // 價(jià)格
@end
- NewGoodsModel.m
#pragma mark - encode
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.goodsId forKey:@"goodsId"]; // 商品ID
[aCoder encodeObject:self.businessId forKey:@"businessId"]; // 商鋪ID
[aCoder encodeObject:self.goodsName forKey:@"goodsName"]; // 商品名稱
[aCoder encodeObject:@(self.price) forKey:@"price"]; // 價(jià)格
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.goodsId = [aDecoder decodeObjectForKey:@"goodsId"];
self.businessId = [aDecoder decodeObjectForKey:@"businessId"];
self.goodsName = [aDecoder decodeObjectForKey:@"goodsName"];
self.price = [[aDecoder decodeObjectForKey:@"price"] doubleValue];
}
return self;
}
@end
JYShopCartViewModel & JYGoodsCartViewModel
- JYShopCartViewModel.h
@class ShopModel, NewGoodsModel;
@interface JYGoodsCartViewModel : NSObject
@property (nonatomic, strong) NewGoodsModel *goodsModel; // 用戶選購(gòu)的商品
@property (nonatomic, assign) NSUInteger quantity; // 用戶選中商品的數(shù)量
@end
/////////////////// 華麗的分割線 /////////////////////////////
@interface JYShopCartViewModel : NSObject
@property (nonatomic, strong) ShopModel *shopModel; // 商家信息
@property (nonatomic, strong) NSArray<JYGoodsCartViewModel *> *goodsArray; // 用戶在當(dāng)前商家所選購(gòu)的所有商品集合
@property (nonatomic, strong) NSDate *createTime; // 添加到購(gòu)物車的時(shí)間
@property (nonatomic, strong) NSDate *updateTime; // 更新時(shí)間
@end
JYShopCart
注意點(diǎn):
用@dynamic修飾屬性,是告訴編譯器,其getter和setter方法會(huì)在程序運(yùn)行的時(shí)候或者用其他方式動(dòng)態(tài)綁定,以便讓編譯器通過編譯,Core Data框架會(huì)在程序運(yùn)行的時(shí)候?yàn)榇祟悓傩陨蒰etter和Setter方法。
-
JYShopCart對(duì)應(yīng)的CoreData結(jié)構(gòu)示意圖:
JYShopCart.png JYShopCart.h
#import <CoreData/CoreData.h>
@interface JYShopCart : NSManagedObject
@property (nonatomic, copy) NSString *cartID; // 唯一標(biāo)識(shí),其實(shí)商鋪的標(biāo)識(shí)
@property (nonatomic, copy) NSString *userID; // 用戶標(biāo)識(shí)
@property (nonatomic, strong) id shopModel; // 商鋪信息
@property (nonatomic, strong) NSData *goodsArrayData; // 所選商品信息集合
@property (nonatomic, strong) NSDate *createTime; // 添加到購(gòu)物車的時(shí)間
@property (nonatomic, strong) NSDate *updateTime; // 更新時(shí)間
/**
* 給當(dāng)前的購(gòu)物車更新信息
*
* shopCart: 新購(gòu)物車信息
*/
- (JYShopCart *)copyInfoByShopCart:(JYShopCart *)shopCart;
@end
- JYShopCart.m
@implementation JYShopCart
/**
用@dynamic修飾屬性,是告訴編譯器,其getter和setter方法會(huì)在程序運(yùn)行的時(shí)候或者用其他方式動(dòng)態(tài)綁定,以便讓編譯器通過編譯,Core Data框架會(huì)在程序運(yùn)行的時(shí)候?yàn)榇祟悓傩陨蒰etter和Setter方法。
*/
@dynamic cartID;
@dynamic userID;
@dynamic shopModel;
@dynamic goodsArrayData;
@dynamic createTime;
@dynamic updateTime;
#pragma mark - actions
- (JYShopCart *)copyInfoByShopCart:(JYShopCart *)shopCart {
self.cartID = [shopCart.cartID copy];
self.userID = [shopCart.userID copy];
self.shopModel = [shopCart.shopModel copy];
self.goodsArrayData = [shopCart.goodsArrayData copy];
self.createTime = [shopCart.createTime copy];
self.updateTime = [shopCart.updateTime copy];
return self;
}
@end
JYGoodsCart
- JYGoodsCart.h
@interface JYGoodsCart : NSObject
@property (nonatomic, copy) NSString *goodsID; // 商品標(biāo)識(shí)
@property (nonatomic, assign) NSUInteger quantity; // 用戶選購(gòu)的商品數(shù)量
@property (nonatomic, strong) NSData *goodsData; // 商品信息
@end
- JYGoodsCart.m
@implementation JYGoodsCart
#pragma mark - encode
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.goodsID forKey:@"goodsID"];
[aCoder encodeObject:@(self.quantity) forKey:@"quantity"];
[aCoder encodeObject:self.goodsData forKey:@"goodsData"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.goodsID = [aDecoder decodeObjectForKey:@"goodsID"];
self.quantity = [[aDecoder decodeObjectForKey:@"quantity"] integerValue];
self.goodsData = [aDecoder decodeObjectForKey:@"goodsData"];
}
return self;
}
@end
JYShopCartViewModel工具類
負(fù)責(zé)對(duì)數(shù)據(jù)庫(kù)的增刪改查;
數(shù)據(jù)模型之間的轉(zhuǎn)換;
更新完數(shù)據(jù)庫(kù)后及時(shí)的通知用戶。
- JYGoodsCart.h
@class JYShopCartViewModel;
UIKIT_EXTERN NSString * const kObserverShopCartDidChange;
@interface JYShopCartHelper : NSObject
+ (instancetype)shareShopCartHelper;
// 查詢
- (NSMutableArray *)loadShopCartInLocal;
- (JYShopCartViewModel *)checkShopCartInLocalWithShopID:(NSString *)shopID;
// 添加
- (void)addGoodsToLocalWithShopCartViewModel:(JYShopCartViewModel *)shopCartViewModel;
- (void)transformShopCartUnLoginToLoginInLocal;
// 更新
- (void)updateGoodsInLocalWithShopCartViewModel:(JYShopCartViewModel *)shopCartViewModel;
// 刪除
- (void)deleteGoodsInLocalWithGoodsArray:(NSArray *)goodsArray shopID:(NSString *)shopID;
- (void)deleteGoodsInLocalWithShopCartViewModel:(JYShopCartViewModel *)shopCartViewModel;
- (void)deleteAllGoodsInLocal;
- JYGoodsCart.m
NSString * const kObserverShopCartDidChange = @"ObserverShopCartDidChange";
static NSString *kUnLoginUserID = @"UnLoginUserID"; // 未登錄的情況下的用戶標(biāo)識(shí),只用于購(gòu)物車中
static NSString *kShowInfo = @"商家信息不全,添加失敗。";
@implementation JYShopCartHelper
+ (instancetype)shareShopCartHelper {
static id shareShopCartHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareShopCartHelper = [[[self class] alloc] init];
});
return shareShopCartHelper;
}
#pragma mark - 查新購(gòu)物車中的所有商品
// 根據(jù)用戶ID查詢數(shù)據(jù)庫(kù)信息,返回改用戶的購(gòu)物車信息
- (NSMutableArray *)loadShopCartInLocal {
NSArray *shopCarts = [self checkShopCartsByUserIDInLocalWithUserID:[self userIDByLogin]];
NSArray *shopCartViewModels = [self transformShopCartsToShopCartViewModels:shopCarts];
return [self sortShopsByCreateTimeOfShopCartWithShopCarts:shopCartViewModels];
}
// 根據(jù)店鋪ID和用戶ID,查詢改店鋪在購(gòu)物車中的選購(gòu)信息
- (JYShopCartViewModel *)checkShopCartInLocalWithShopID:(NSString *)shopID {
JYShopCart *shopCart = [self checkShopCartInLocalWithShopID:shopID userID:[self userIDByLogin]];
return [self transformShopCartToShopCartViewModel:shopCart];
}
// 用戶可以選擇按時(shí)間的排序類型
- (NSMutableArray *)sortShopsByCreateTimeOfShopCartWithShopCarts:(NSArray *)shopCarts {
NSArray *sorts = [shopCarts sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
JYShopCartViewModel *model1 = obj1;
JYShopCartViewModel *model2 = obj2;
// 根據(jù)更新時(shí)間排序,倒敘
if (model1.updateTime == [model1.updateTime earlierDate:model2.updateTime]) {
return NSOrderedDescending; // 降序
} else if (model1.updateTime == [model1.updateTime laterDate:model2.updateTime]) {
return NSOrderedAscending; // 升序
} else {
return NSOrderedSame; // 相等
}
}];
return [sorts mutableCopy];
}
// 根據(jù)用戶ID,查詢數(shù)據(jù)庫(kù)信息
- (NSArray *)checkShopCartsByUserIDInLocalWithUserID:(NSString *)userID {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.userID == %@", userID];
return [JYShopCart MR_findAllWithPredicate:predicate];
}
// 根據(jù)商鋪ID和用戶ID,查詢數(shù)據(jù)庫(kù)信息
- (JYShopCart *)checkShopCartInLocalWithShopID:(NSString *)shopID userID:(NSString *)userID {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.cartID == %@ && SELF.userID == %@", shopID, userID];
return [JYShopCart MR_findFirstWithPredicate:predicate];
}
#pragma mark - 添加商品到購(gòu)物車
- (void)addGoodsToLocalWithShopCartViewModel:(JYShopCartViewModel *)shopCartViewModel {
if (!shopCartViewModel.shopModel || shopCartViewModel.shopModel.dmId.length <= 0) {
[SVProgressHUD showInfoWithStatus:kShowInfo];
return;
}
JYShopCart *shopCart = [self checkShopCartInLocalWithShopID:shopCartViewModel.shopModel.dmId userID:[self userIDByLogin]];
if (!shopCart) {
shopCart = [JYShopCart MR_createEntity];
} else {}
shopCart.userID = [self userIDByLogin];
shopCart.cartID = shopCartViewModel.shopModel.dmId;
shopCart.shopModel = shopCartViewModel.shopModel;
shopCart.goodsArrayData = [self archivedDataWithGoodsArray:shopCartViewModel.goodsArray];
shopCart.updateTime = [NSDate date];
shopCart.createTime = [NSDate date];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
[self postNotificationAfterShopCartIsChange];
}
// 未登錄選購(gòu)的商品 ===> 用戶登錄后的購(gòu)物車中
- (void)transformShopCartUnLoginToLoginInLocal {
NSArray *shopCarts = [self checkShopCartsByUserIDInLocalWithUserID:kUnLoginUserID];
for (JYShopCart *cart in shopCarts) {
cart.userID = [self userIDByLogin];
}
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
[self deleteAllShopCartInLocalWithUserID:kUnLoginUserID];
[self postNotificationAfterShopCartIsChange];
}
#pragma mark - 更新購(gòu)物車中的商品
- (void)updateGoodsInLocalWithShopCartViewModel:(JYShopCartViewModel *)shopCartViewModel {
if (!shopCartViewModel.shopModel || shopCartViewModel.shopModel.dmId.length <= 0) {
[SVProgressHUD showInfoWithStatus:kShowInfo];
return;
}
[self addGoodsToLocalWithShopCartViewModel:shopCartViewModel];
[self postNotificationAfterShopCartIsChange];
}
#pragma makrk - 刪除購(gòu)物車中的商品
// 從購(gòu)物車中,刪除店鋪中的商品集合
- (void)deleteGoodsInLocalWithGoodsArray:(NSArray *)goodsArray shopID:(NSString *)shopID {
if (shopID.length <= 0 || goodsArray.count <= 0) {
return;
}
JYShopCartViewModel *shopCartViewModel = [self checkShopCartInLocalWithShopID:shopID];
NSMutableArray *newGoodsArray = [shopCartViewModel.goodsArray mutableCopy];
for (JYGoodsCartViewModel *deleteGoods in goodsArray) {
for (JYGoodsCartViewModel *goods in shopCartViewModel.goodsArray) {
if ([deleteGoods.goodsModel.goodsId isEqualToString:goods.goodsModel.goodsId]) {
[newGoodsArray removeObject:goods];
}
}
}
if (newGoodsArray.count <= 0) {
[self deleteShopCartInLocalWithShopID:shopID UserID:[self userIDByLogin]];
} else {
shopCartViewModel.goodsArray = [newGoodsArray copy];
[self updateGoodsInLocalWithShopCartViewModel:shopCartViewModel];
}
}
// 刪除購(gòu)物車中,此商鋪的信息
- (void)deleteGoodsInLocalWithShopCartViewModel:(JYShopCartViewModel *)shopCartViewModel {
if (!shopCartViewModel.shopModel || shopCartViewModel.shopModel.dmId.length <= 0) {
[SVProgressHUD showInfoWithStatus:kShowInfo];
return;
}
[self deleteShopCartInLocalWithShopID:shopCartViewModel.shopModel.dmId UserID:[self userIDByLogin]];
[self postNotificationAfterShopCartIsChange];
}
// 清空購(gòu)物車
- (void)deleteAllGoodsInLocal {
[self deleteAllShopCartInLocalWithUserID:[self userIDByLogin]];
[self postNotificationAfterShopCartIsChange];
}
- (void)deleteAllShopCartInLocalWithUserID:(NSString *)userID {
NSArray *localShopCarts = [self checkShopCartsByUserIDInLocalWithUserID:userID];
for (JYShopCart *cart in localShopCarts) {
[cart MR_deleteEntity];
}
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
}
- (void)deleteShopCartInLocalWithShopID:(NSString *)shopID UserID:(NSString *)userID {
JYShopCart *shopCart = [self checkShopCartInLocalWithShopID:shopID userID:userID];
if (shopCart) {
[shopCart MR_deleteEntity];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
}
}
#pragma mark - 轉(zhuǎn)換
// JYShopCart ==> JYShopCartViewModel
- (JYShopCartViewModel *)transformShopCartToShopCartViewModel:(JYShopCart *)shopCart {
if (!shopCart) {
return nil;
}
JYShopCartViewModel *shopCartViewModel = [[JYShopCartViewModel alloc] init];
shopCartViewModel.shopModel = shopCart.shopModel;
shopCartViewModel.goodsArray = [self unArchivedGoodsArrayWithData:shopCart.goodsArrayData];
shopCartViewModel.createTime = shopCart.createTime;
shopCartViewModel.updateTime = shopCart.updateTime;
return shopCartViewModel;
}
// shopCart集合 ==> shopCartViewModel集合
- (NSArray *)transformShopCartsToShopCartViewModels:(NSArray *)shopCarts {
if (shopCarts.count <= 0) {
return nil;
}
NSMutableArray *shopCartViewModels = [NSMutableArray array];
for (JYShopCart *shopCart in shopCarts) {
JYShopCartViewModel *model = [self transformShopCartToShopCartViewModel:shopCart];
[shopCartViewModels addObject:model];
}
return shopCartViewModels;
}
#pragma mark - 獲取用戶標(biāo)識(shí)
- (NSString *)userIDByLogin {
if ([OPTRuntime sharedInstance].currentNewUser.logined && [OPTRuntime sharedInstance].currentNewUser.uid.length > 0) {
return [OPTRuntime sharedInstance].currentNewUser.uid;
}
return kUnLoginUserID;
}
#pragma mark - 商品的歸檔和解檔
- (NSData *)archivedDataWithGoodsArray:(NSArray *)goodsArray {
if (goodsArray.count > 0) {
NSMutableArray *carts = [NSMutableArray array];
for (JYGoodsCartViewModel *model in goodsArray) {
JYGoodsCart *goodsCart = [[JYGoodsCart alloc] init];
goodsCart.goodsID = model.goodsModel.goodsId;
goodsCart.goodsData = [self archivedDataWithGoodsModel:model.goodsModel];
goodsCart.quantity = model.quantity;
[carts addObject:goodsCart];
}
return [NSKeyedArchiver archivedDataWithRootObject:carts];
}
return nil;
}
- (NSArray *)unArchivedGoodsArrayWithData:(NSData *)goodsArrayData {
if (goodsArrayData) {
NSArray *carts = [NSKeyedUnarchiver unarchiveObjectWithData:goodsArrayData];
NSMutableArray *goodsViewModels = [NSMutableArray array];
for (JYGoodsCart *cart in carts) {
JYGoodsCartViewModel *model = [[JYGoodsCartViewModel alloc] init];
model.goodsModel = [self unArchivedWithData:cart.goodsData];
model.quantity = cart.quantity;
[goodsViewModels addObject:model];
}
return goodsViewModels;
}
return nil;
}
- (NSData *)archivedDataWithGoodsModel:(NewGoodsModel *)goodsModel {
if (goodsModel) {
return [NSKeyedArchiver archivedDataWithRootObject:goodsModel];
}
return nil;
}
- (NewGoodsModel *)unArchivedWithData:(NSData *)goodsData {
if (goodsData) {
return [NSKeyedUnarchiver unarchiveObjectWithData:goodsData];
}
return nil;
}
#pragma mark - 發(fā)送更新購(gòu)物車的通知
- (void)postNotificationAfterShopCartIsChange {
[[NSNotificationCenter defaultCenter] postNotificationName:kObserverShopCartDidChange object:nil];
}
@end
結(jié)尾:
以上是個(gè)人對(duì)購(gòu)物車的簡(jiǎn)單理解,局限于水平的問題,可能還存在一些問題,如有疑問或建議,歡迎積極留言探討。>v<
- 個(gè)人github: https://github.com/SilongLi

