http://www.javashuo.com/article/p-zmycnkvk-g.html
美多商城商業模式:
C2B模式(消費者到企業的商業模式),相相似網站包括:京東,淘寶,海爾商城,尚品宅配等。html
商城需求分析
1,用戶部分
2,商品部分
3,購物車部分
4,商品訂單備份
5,用戶支付部分
6,上線程序的配置前端
基本功能:用戶註冊,登陸,密碼的重置,第三方登陸
用戶註冊
1,圖片驗證碼
流程分析:
1,前端生成uuid隨機字符串
2,後端生成圖片驗證碼發送給前端,將圖形驗證碼的存入到redis中
2,短信驗證碼
1,檢查圖片的驗證碼
2,檢驗是不是在60s內是否已經發送過
3,生成短信驗證碼
4,保存發送的短信驗證碼
5,發送短信(第三方平臺發送:雲通信)
3,判斷用戶名是否存在
1,用戶輸入用戶名以後ajax局部刷新頁面
2,後臺查詢數據庫用戶是否存在
3,返回數據給前端
4,判斷手機號碼是否已經存在
同3python
cookies的使用目的web
傳統的cookies顯示出來的問題
- 在如今的市場各類不一樣的瀏覽器,對同一個網站進行,用戶的每種設備都須要維護相關的session在服務器端,會形成服務器資源的浪費,相關網站採起單點登陸來記錄用戶的狀態狀態來解決以上傳統cookies帶來的問題ajax
單點登陸的概念redis
token認證的機制docker
備註:數據庫
用戶登陸JWT認證的的流程源代碼(待繼續理解)django
qq登陸後端
qq登陸流程oauth2的認證流程分析
附件 0.00KB
忘記密碼的功能的實現
我的信息
我的信息是用戶的私有信息,必須是登陸用戶才能夠訪問,而且值能夠訪問本身的相關信息
用戶我的中心的信息中有一項是用戶的郵箱是否激活
郵箱驗證的流程
from rest_framework import serializers
from .models import Area
class AreaSerializer(serializers.ModelSerializer):
""" 行政區劃信息序列化器 """
class Meta:
model = Area
fields = ('id', 'name')
class SubAreaSerializer(serializers.ModelSerializer):
""" 子行政區劃信息序列化器 """
subs = AreaSerializer(many=True, read_only=True)
class Meta:
model = Area
fields = ('id', 'name', 'subs')
基本功能:提交訂單,個人訂單,訂單評價
電商類型的網站首頁的訪問評率較大,每次獲取首頁過程當中去對數據庫進行查詢顯然不太合理,除了使用傳統意義上的緩存實現以外,咱們可使用首頁靜態化的技術,即將動態生成的html頁面生成保存成靜態文件,在用戶訪問的過程當中直接將靜態文件發送給用戶
優勢:能夠緩解數據庫壓力,而且能夠提升用戶訪問的網站的速度,提升用戶體驗
帶來的問題是:用戶在頁面中動態生成的部分數據如何處理
應用場景:常常容易訪問可是,數據的變更並非太大的一些頁面能夠考慮使用靜態化技術
難點GoodsCategory,GoodsChannel兩個表格之間的關係設計以及獲取商城商品分類的菜單
難點 商品詳情頁面的數據結構
#!/usr/bin/env python
""" 功能:手動生成全部SKU的靜態detail html文件 使用方法: ./regenerate_detail_html.py """
import sys
sys.path.insert(0, '../')
sys.path.insert(0, '../meiduo_mall/apps')
import os
if not os.getenv('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_mall.settings.dev'
import django
django.setup()
from django.template import loader
from django.conf import settings
from goods.utils import get_categories
from goods.models import SKU
def generate_static_sku_detail_html(sku_id):
""" 生成靜態商品詳情頁面 :param sku_id: 商品sku id """
# 商品分類菜單
categories = get_categories()
# 獲取當前sku的信息
sku = SKU.objects.get(id=sku_id)
sku.images = sku.skuimage_set.all()
# 麪包屑導航信息中的頻道
goods = sku.goods
goods.channel = goods.category1.goodschannel_set.all()[0]
# 構建當前商品的規格鍵
sku_specs = sku.skuspecification_set.order_by('spec_id')
sku_key = []
for spec in sku_specs:
sku_key.append(spec.option.id)
print("當前商品的規格鍵[1,4,7]",sku_key)
# 獲取當前商品的全部SKU
skus = goods.sku_set.all()
print("獲取當前商品的所在的SPU下的全部SKU對象",skus)
# 構建不一樣規格參數(選項)的sku字典
# spec_sku_map = {
# (規格1參數id, 規格2參數id, 規格3參數id, ...): sku_id,
# (規格1參數id, 規格2參數id, 規格3參數id, ...): sku_id,
# ...
# }
spec_sku_map = {}
for s in skus:
# 獲取sku的規格參數
s_specs = s.skuspecification_set.order_by('spec_id')
# 用於造成規格參數-sku字典的鍵
key = []
for spec in s_specs:
key.append(spec.option.id)
# 向規格參數-sku字典添加記錄
# print("構造出的不一樣規格的參數",key)
spec_sku_map[tuple(key)] = s.id
print("{(1, 4, 7): 1, (1, 3, 7): 2}構造出的不一樣規格的參數",spec_sku_map)
# 獲取當前商品的規格信息
specs = goods.goodsspecification_set.order_by('id')
print("當前商品全部的規格選項,屏幕,顏色,,,",specs)
# print("sku_key",sku_key)
# 若當前sku的規格信息不完整,則再也不繼續
if len(sku_key) < len(specs):
return
for index, spec in enumerate(specs):
# if index == 0:
# print("index", index)
# print("GoodsSpecification的規格信息對象", spec)
# 複製當前sku的規格鍵
key = sku_key[:]
print("當前的規格選項",spec.name)
# 該規格的選項
options = spec.specificationoption_set.all()
print("options規格信息的選項", options)
for option in options:
# 在規格參數sku字典中查找符合當前規格的sku
print("001",key)
print("固定不變的數據庫中只有這兩種商品spec_sku_map",spec_sku_map)
print("option.id", option.id)
key[index] = option.id
print("002",key)
option.sku_id = spec_sku_map.get(tuple(key))
print(option.sku_id)
spec.options = options
# 渲染模板,生成靜態html文件
context = {
'categories': categories,
'goods': goods,
'specs': specs,
'sku': sku
}
template = loader.get_template('detail.html')
html_text = template.render(context)
file_path = os.path.join(settings.GENERATED_STATIC_HTML_FILES_DIR, 'goods/'+str(sku_id)+'.html')
with open(file_path, 'w') as f:
f.write(html_text)
if __name__ == '__main__':
sku = SKU.objects.get(id=1)
generate_static_sku_detail_html(sku.id)
當前商品的規格鍵[1,4,7] [1, 4, 7]
獲取當前商品的所在的SPU下的全部SKU對象 <QuerySet [<SKU: 1: Apple MacBook Pro 13.3英寸筆記本 銀色>, <SKU: 2: Apple MacBook Pro 13.3英寸筆記本 深灰色>]>
{(1, 4, 7): 1, (1, 3, 7): 2}構造出的不一樣規格的參數 {(1, 4, 7): 1, (1, 3, 7): 2}
當前商品全部的規格選項,屏幕,顏色,,, <QuerySet [<GoodsSpecification: Apple MacBook Pro 筆記本: 屏幕尺寸>, <GoodsSpecification: Apple MacBook Pro 筆記本: 顏色>, <GoodsSpecification: Apple MacBook Pro 筆記本: 版本>]>
當前的規格選項 屏幕尺寸
options規格信息的選項 <QuerySet [<SpecificationOption: Apple MacBook Pro 筆記本: 屏幕尺寸 - 13.3英寸>, <SpecificationOption: Apple MacBook Pro 筆記本: 屏幕尺寸 - 15.4英寸>]>
001 [1, 4, 7]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 1
002 [1, 4, 7]
1
001 [1, 4, 7]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 2
002 [2, 4, 7]
None
當前的規格選項 顏色
options規格信息的選項 <QuerySet [<SpecificationOption: Apple MacBook Pro 筆記本: 顏色 - 深灰色>, <SpecificationOption: Apple MacBook Pro 筆記本: 顏色 - 銀色>]>
001 [1, 4, 7]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 3
002 [1, 3, 7]
2
001 [1, 3, 7]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 4
002 [1, 4, 7]
1
當前的規格選項 版本
options規格信息的選項 <QuerySet [<SpecificationOption: Apple MacBook Pro 筆記本: 版本 - core i5/8G內存/256G存儲>, <SpecificationOption: Apple MacBook Pro 筆記本: 版本 - core i5/8G內存/128G存儲>, <SpecificationOption: Apple MacBook Pro 筆記本: 版本 - core i5/8G內存/512G存儲>]>
001 [1, 4, 7]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 5
002 [1, 4, 5]
None
001 [1, 4, 5]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 6
002 [1, 4, 6]
None
001 [1, 4, 6]
固定不變的數據庫中只有這兩種商品spec_sku_map {(1, 4, 7): 1, (1, 3, 7): 2}
option.id 7
002 [1, 4, 7]
1
specs = [
{
'name': '屏幕尺寸',
'options': [
{'value': '13.3寸', 'sku_id': xxx},
{'value': '15.4寸', 'sku_id': xxx},
]
},
{
'name': '顏色',
'options': [
{'value': '銀色', 'sku_id': xxx},
{'value': '黑色', 'sku_id': xxx}
]
},
...
]
specs = [
{
'name': '屏幕尺寸',
'options': [
{'value': '13.3寸', 'sku_id': Iphone7的sku_id},
{'value': '15.4寸', 'sku_id': Iphone6的sku_id},
{'value': '15.4寸', 'sku_id': Iphone7的sku_id2},
]
},
{
'name': '顏色',
'options': [
{'value': '銀色', 'sku_id': Iphone7的sku_id},
{'value': '銀色', 'sku_id': Iphone6的sku_id},
{'value': '金色', 'sku_id': Iphone7的sku_id2},
]
},
...
]
前端經過傳入的sku id來取值生成的詳情頁面,從同一份數據數據中拿的值,就會避免重複,例如Iphone6的只是取出全部的Iphone6的全部的信息生成靜態頁面,傳入的sku id不一樣獲得的頁面效果不一樣,經過不一樣的id也能夠找到不一樣的商品的詳情頁面
細節完善在運營人員相關修改商品信息,要在後端實現,自動刷新詳情的頁面的數據,自動觸發靜態頁面的生成,利用了django中的ModelAdmin,在數據發生變更以後自動進行更新相關數據
class SKUSpecificationAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
from celery_tasks.html.tasks import generate_static_sku_detail_html
generate_static_sku_detail_html.delay(obj.sku.id)
def delete_model(self, request, obj):
sku_id = obj.sku.id
obj.delete()
from celery_tasks.html.tasks import generate_static_sku_detail_html
generate_static_sku_detail_html.delay(sku_id)
class CartMixin():
def str2dict(self, redis_data): # redis_data從redis中讀取的數據
""" 轉化爲python字典"""
redis_dict = pickle.loads(base64.b64decode(redis_data))
return redis_dict
def dict2str(self, redis_dict):
""" python中dict轉爲能夠存入redis中的數據"""
# 將合併後的字典再次存入到redis中
redis_bytes = pickle.dumps(redis_dict)
redis_str = base64.b64encode(redis_bytes)
return redis_str
def write_cart(self, request: Request, response: Response, cart_dict: dict):
""" 寫購物車數據"""
cart_str = self.dict2str(cart_dict)
if request.user.is_authenticated:
key = "cart2_%s" % request.user.id
get_redis_connection("cart").set(key, cart_str)
else:
response.set_cookie("cart", cart_str)
def read_from_redis(self, user_id):
""" 返回一個字典"""
key = "cart2_%s" % user_id
redis_data = get_redis_connection("cart").get(key)
if redis_data is None:
return {}
return self.str2dict(redis_data)
def read_from_cookie(self, request: Request):
value = request.COOKIES.get("cart")
if value is None:
return {}
return self.str2dict(value)
def read(self, request: Request) -> dict:
if request.user.is_authenticated:
cart_dict = self.read_from_redis(request.user.id)
else:
cart_dict = self.read_from_cookie(request)
return cart_dict
class CartView(CartMixin, APIView):
# pagination_class = StandardResultsSerPagination
def post(self, request):
serializer = CartSerializer(data=request.data) # 檢查前端發送過來數據是否正確
serializer.is_valid(raise_exception=True) # 數據檢驗經過
sku_id = serializer.validated_data['sku_id']
count = serializer.validated_data['count']
selected = serializer.validated_data["selected"]
cart_dict = self.read(request)
if sku_id in cart_dict:
#
new_count = cart_dict[sku_id][0] + count
cart_dict[sku_id] = [new_count, selected]
else:
cart_dict[sku_id] = [count, selected]
resp = Response(serializer.data, status=status.HTTP_201_CREATED)
self.write_cart(request, resp, cart_dict)
return resp
def get(self, request):
cart_dict = self.read(request)
skus = SKU.objects.filter(id__in=cart_dict.keys())
for sku in skus:
sku.count = cart_dict[sku.id][0]
sku.selected = cart_dict[sku.id][1]
serializer = CartSKUSerializer(skus, many=True)
return Response(serializer.data)
def put(self, request):
# 檢查前端發送的數據是否正確
serializer = CartSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
sku_id = serializer.validated_data.get('sku_id')
count = serializer.validated_data.get('count')
selected = serializer.validated_data.get('selected')
cart_dict = self.read(request)
cart_dict[sku_id] = [count, selected]
resp = Response(serializer.data, status=status.HTTP_201_CREATED)
self.write_cart(request, resp, cart_dict)
return resp
def delete(self, request):
# 檢查參數sku_id
serializer = CartDeleteSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
sku_id = serializer.validated_data['sku_id']
cart_dict = self.read(request)
if sku_id in cart_dict:
del cart_dict[sku_id]
resp = Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
self.write_cart(request, resp, cart_dict)
return resp
def merge_cart_cookie_to_redis(request, response, user):
""" 合併購物車"""
cookies_cart = request.COOKIES.get('cart')
if cookies_cart is not None:
cookies_dict = pickle.loads(base64.b64decode(cookies_cart)) # 取出cookies中的數據
print("cookies_dict000", cookies_dict)
redis_conn = get_redis_connection("cart")
redis_cart = redis_conn.get("cart2_%s" % user.id) #
redis_dict = {}
if redis_cart:
redis_dict = pickle.loads(base64.b64decode(redis_cart)) # 取出的是redis中的數據
print("redis_dict001", redis_dict)
# 過濾購物車中沒有被選中的商品
new_cookies_dict = deepcopy(cookies_dict)
for sku_id, value in cookies_dict.items():
if not value[1]:
new_cookies_dict.pop(sku_id)
print("redis_dict002", new_cookies_dict)
# 合併cookies中的值
redis_dict.update(new_cookies_dict)
print("redis_dict003", redis_dict)
redis_str = base64.b64encode(pickle.dumps(redis_dict))
key = "cart2_%s" % user.id
get_redis_connection("cart").set(key, redis_str)
# 往redis中寫入數據
# if cart:
# pl = redis_conn.pipeline()
# pl.hmset("cart_%s" % user.id, cart)
# pl.sadd("cart_selected_%s" % user.id, *redis_cart_selected)
# pl.execute()
# 刪除cookie中的數據
response.delete_cookie("cart")
return response
return response
基本功能:支付寶支付流程分析