如下轉載於http://www.javashuo.com/article/p-zmycnkvk-g.html 僅供本人學習和研究html
商城商業模式:
C2B模式(消費者到企業的商業模式),相相似網站包括:京東,淘寶,海爾商城,尚品宅配等。前端
商城需求分析
1,用戶部分
2,商品部分
3,購物車部分
4,商品訂單備份
5,用戶支付部分
6,上線程序的配置python
用戶部分模塊:
基本功能:用戶註冊,登陸,密碼的重置,第三方登陸
用戶註冊
1,圖片驗證碼
流程分析:
1,前端生成uuid隨機字符串
2,後端生成圖片驗證碼發送給前端,將圖形驗證碼的存入到redis中
2,短信驗證碼
1,檢查圖片的驗證碼
2,檢驗是不是在60s內是否已經發送過
3,生成短信驗證碼
4,保存發送的短信驗證碼
5,發送短信(第三方平臺發送:雲通信)
3,判斷用戶名是否存在
1,用戶輸入用戶名以後ajax局部刷新頁面
2,後臺查詢數據庫用戶是否存在
3,返回數據給前端
4,判斷手機號碼是否已經存在
同3git
技術點:先後端的域名不相同,涉及到csrf跨站請求僞造的問題;
- Csrf相關概念:
1,域=協議+域名+端口,在兩個域中,以上三者中任意一個條件不一樣,均涉及到跨域的問題;
2,瀏覽器的策略
1,對於簡單的請求,瀏覽器發送請求,可是獲得請求以後會檢驗響應頭中是否有當前的域中,若是沒有則會在瀏覽器中報錯;
2,對於複雜的請求,瀏覽器會先發送一個option請求,詢問服務器是否支持跨域,若是響應頭中的域名容許,纔會發送相對應的請求來獲取數據,並交給js進行處理。
3,Python的django中的跨域處理的相關模塊django-cors-headers
技術點:前端用戶將圖片驗證碼發送給後臺以後,第三方平臺發送短信的過程當中會有網絡的阻塞程序繼續往下執行,進而影響用戶體驗效果;
- 解決方案:採用celery進行短信驗證碼的異步發送;
Celery概念:分佈式異步任務隊列調度框架:
1,支持定時任務和異步任務兩種方式
2,組成:大概分爲四個部分client客戶端發送數據,broker中間件(redis數據庫,消息隊列),worker(任務的執行者),backend(執行worker任務的執行結果)
3,能夠開啓多進程也能夠是多線程
4,應用場景:在某一個任務的執行過程當中,會涉及到耗時的操做,可是這個耗時操做並不會影響後續的程序的執行,此時就能夠用celery來異步執行這些任務;
用戶登陸
JWTtoken的相關了解
-
cookies的使用目的ajax
- http協議本生是一種無狀態的協議,假如用戶每次發送請求過來將用戶名和密碼在後端進行驗證後才能夠登陸到某些界面才能夠進行操做,當客戶端再次請求服務器時,又要從新進行認證;
- 解決方法:在客戶端設置cookie並在本地設置session存儲用戶的敏感信息,從而來記錄當前用戶登陸的狀態,若是用戶量再次請求服務器,將cookie帶給服務器,服務器查詢session獲取用戶信息,進行下一步操做。
- 客戶端比較多的狀況下,seession中默認會存在內存,隨着用戶量的增長服務器的壓力就會變大;
-
傳統的cookies顯示出來的問題
- 在如今的市場各類不一樣的瀏覽器,對同一個網站進行,用戶的每種設備都須要維護相關的session在服務器端,會形成服務器資源的浪費,相關網站採起單點登陸來記錄用戶的狀態狀態來解決以上傳統cookies帶來的問題redis
-
單點登陸的概念sql
- 用戶在不一樣的設備中進行登陸,服務器端不用維護用戶的相關信息,在每次登陸的過程當中都由客戶端將本身的用戶信息發送過來,服務端進行解析來獲取用戶的相關信息
-
token認證的機制docker
- 用戶攜帶用戶名和密碼來後端進行驗證
- 服務器端驗證經過後對爲當前用戶生成token
- 將token返回給前端,記錄用戶的信息
- 用戶再次請求服務器的時候,服務端解析token相關的信息,驗證用戶
- 肯定用戶狀態,進行 相關操做
-
備註:數據庫
- jwt的組成第一部分咱們稱它爲頭部(header),第二部分咱們稱其爲載荷(payload, 相似於飛機上承載的物品),第三部分是簽證(signature).
- secretkey是存儲在服務器端的,若是secret key
-
用戶登陸JWT認證的的流程源代碼(待繼續理解)django
-
qq登陸
-
qq登陸流程oauth2的認證流程分析
附件0.00KB
- 用戶向美多網站發送qq註冊的請求
- 美多網站向用戶返回qq登陸的頁面
- 用戶在qq登陸的界面向qq的服務器發送qq用戶名和密碼發起登陸的請求
- qq服務器認證成功以後將用戶引導到回調的網址中,並返回給用戶qq服務器的token值
- 用戶重定向到美多頁面並攜帶了qq服務器發送的token
- 後端接收到token後向qq服務器請求access token
- qq服務器返回access token
- 美多服務器經過access token來向qq服務器來獲取用戶的openid
- 經過id來查詢用戶是否已經在美多商城註冊
- 用戶已經註冊直接返回用戶的access token值
- 用戶沒有帳號,生成註冊的access token,(載荷openid)從新註冊信息發送給後端
- 後端接收到數據以後建立對象並將qq用戶的openid和帳號進行綁定
-
忘記密碼的功能的實現
- 用戶發送過來請求,攜帶用戶名和圖片驗證碼
- 後端驗證圖片驗證碼,經過帳號查詢用戶,將用戶的電話號碼部分返回給前端,並生成發送短信的access token(載荷mobile)值
- 前端填寫手機號碼驗證碼並攜帶access token到後端
- 後端接收到手機號碼校驗(正確性,發送時間間隔),經過手機號碼站到用戶對象,生成密碼修改的access token(載荷uer obj)值
- 前端用戶填寫新的密碼以後,攜帶access token到後端重置密碼
技能點
- djangorestframework中的實現JWT token的模塊itsdangerous的使用
用戶中心
技術點:
- django中發送郵件的配置信息
- 利用celery來實現異步的郵件發送
用戶收貨地址的設置
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')
- DRF中的ReadOnlyModelViewSet中將請求方式與資源狀態進行了綁定,在這裏咱們只須要從數據庫中去數據因此直接就能夠繼承ModelSerializer這個類
- view視圖中的action=='list'(即前端url不帶參數)說明前端要獲取全部的省份
- view視圖中的action!='list'(即前端url帶參數)說明前端要獲取全部的省份底下的行政區劃
- 在這個返回的過程當中若是前端頁面返回的url中返回的帶有參數則返回省份
技術點
- DRF的擴展類中的選擇以及序列化器的嵌套調用方法
- 對DRF的擴展集的理解
- Views django 中的原始的Views
- APIView繼承類django中的Views,同時提供了用戶認證,權限認證,權限認證,節流認證,分頁,序列化等方法
- GenericAPIView 繼承了APIView:在這個類中實現了兩個類實行和三個類方法
- ListModelMixin 實現了list方法與get_queryset(),paginate_queryset,get_serializer
- ListAPIView 可用的子類GenericAPIView、ListModelMixin 是上面兩種方法的子類
- ViewSetMixin 實現了view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
訂單模塊:
基本功能:提交訂單,個人訂單,訂單評價
FastDFS分佈式文件系統
- FastDFS分佈式文件系統,數據冗餘,數據的備份,數據量的存儲擴展
- tracker server的做用是負載均衡和調度,能夠實現集羣,每一個reacker節點的地位平等,收集storage的狀態;
- storage server的做用是存儲,不一樣的組內保存的內容是不一樣的,相同的組內保存的內容是相同的,這樣的設計數據會相對比較安全安全;
- 不管是tracker仍是storage都支持集羣的方式擴展,數據的擴展比較方便
- 文件上傳的流程
- storage server定時向tracker server的上傳存儲狀態信息
- 客戶端上傳連接請求
- 請求會先到達tracker server,tracker server查詢能夠調用的storage;
- 返回storage server 的ip和port
- 上傳文件到指定的storage server中
- srorage server生成file id,並將文件存入到磁盤中
- 返回file id給客戶端
- 存儲文件信息
docker的理解
- docker是一種虛擬化的技術,咱們能夠將docker視爲一種容器,在容器的內部能夠運行服務,
- Docker自己是一種C/S架構的程序,Docker客戶端須要向服務器發送請求,服務器完成全部的工做以後返回給客戶端結果;
- 優勢
- 加速本地開發和構建的流程,在本地能夠本身輕鬆的構建,運行,分享所配置好的docker環境
- 可以在不一樣的操做系統的環境中獲取相同的docker容器中的環境,減小了在部署環節中的環境問題待來的麻煩
- docker能夠建立虛擬化的沙箱環境能夠供測試使用
- docker可讓開發者在最開始的開發過程當中在測試的環境中運行,而並不是一開始就在生產環境中開發,部署和測試
首頁靜態化的技術
-
電商類型的網站首頁的訪問評率較大,每次獲取首頁過程當中去對數據庫進行查詢顯然不太合理,除了使用傳統意義上的緩存實現以外,咱們可使用首頁靜態化的技術,即將動態生成的html頁面生成保存成靜態文件,在用戶訪問的過程當中直接將靜態文件發送給用戶
-
優勢:能夠緩解數據庫壓力,而且能夠提升用戶訪問的網站的速度,提升用戶體驗
-
帶來的問題是:用戶在頁面中動態生成的部分數據如何處理
- 在靜態頁面加載完成以後,經過js的代碼將動態的請求發送到後天請求數據,可是大部分數據均是靜態頁面中的數據,
- 靜態生成的頁面由於並無實時更新,會出現部分商品的靜態化頁面中的數據和數據庫中實時更新的數據有差別
-
應用場景:常常容易訪問可是,數據的變更並非太大的一些頁面能夠考慮使用靜態化技術
-
難點GoodsCategory,GoodsChannel兩個表格之間的關係設計以及獲取商城商品分類的菜單
- 首先是GoodsChannel,將全部的商品的頻道取出按照組號和組內順序排序
- 排序後將數據以categories[group_id] = {'channels': [], 'sub_cats': []}的形式存入到有個有序的字典中
- channels的值爲存儲的是頻道的相關信息(例如手機,相機,數碼)
- sub_cats中存儲的值是該頻道下的GoodsCategory相關信息(例如手機通信,手機配件...相關,根據GoodsChannel數據結構表中頂級類別來查詢子類別)
- 分別爲頂級類別下的子類別對象添加一個sub_cats的列表,來存儲此類別下的全部GoodsCategory的queryset對象
-
難點 商品詳情頁面的數據結構
當前商品的規格鍵[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} ] }, ... ]
- 經過sku id來取出此sku商品的SPU對應的全部存在的商品組合
- 循環數據庫中全部的商品選項,將商品的選項與sku id來作對應,返回上面的數據類型
- 相同的SPU對應的不一樣的SKU,返回的specs是相同的,例如若是同屬於手機這個SPU的Iphone6手機和Iphone7手機,返回的specs是相同的,若假設手機品牌只有屏幕大小不相同,則返回的數據類型以下
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)
獲取熱銷商品
- 技術點
- 詳情頁面中的熱銷商品每一個頁面加載完成以後都會來向後端請求數據,可是熱銷商品卻不常常發生變化或者是在一段時間內根據相關字段統計生成返回給前端便可,全部使用緩存的方式存儲熱銷商品是最合理的方式,避免了數據的連接,減小了服務器的壓力,充分的利用了緩存的響應速度也比較快能夠提升用戶的體驗
商品列表頁面的展現
- 商品數據動態的從後端獲取,其餘部分生成靜態化頁面
- 技術點:
- DRF框架中過濾,排序,分頁,序列化器數據的返回
- 適當使用到了DRF提供的擴展類ListAPIView來簡化代碼
商品搜索的功能實現
- 技術點
- Elasticsearch搜索引擎的原理:經過搜索引擎進行搜索數據查詢的過程當中,搜索引擎並非直接去數據庫中去進行數據庫的查詢,而是搜素引擎會對數據庫中全部的數據進行一遍的預處理,單獨的創建一份索引表,在進行數據庫查詢的過程當中,會在索引表中進行查詢,而後返回相應的數據。
- Elasticsearch 不支持對中文進行分詞創建索引,須要配合擴展elasticsearch-analysis-ik來實現中文分詞處理
- 使用haystack對接Elasticsearch的流程
- 安裝
- 在配置文件中配置haystack使用的搜索引擎後端
- SKU索引數據模型類
- 在templates目錄中建立text字段使用的模板文件
- 手動生成初始索引
- 建立序列化器
- 建立視圖
- 定義路由
購物車模塊
class CartMixin(): def str2dict(self, redis_data):
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中的數據類型格式與redis數據庫中數據類型設計成形同的
- 對redis數據庫的相關操做
- {sku id :[count,True]}數據中sku id:商品的id,count:購物車中數據的商品的個數;True或者False表明是否被選中
- 將對數據的操做封裝成一個擴展集,在視圖中繼承擴展類
訂單相關的操做
- 訂單數據庫表的設計
- 訂單的字段分析
- 首先將訂單分爲兩個表格
- 1,訂單的基本信息表;
- 2,訂單的商品信息,二者之間的關係是一對多的關係
- 訂單的基本信息表中主要存儲這筆訂單的相關信息(訂單的id,下單的用戶,用戶的默認地址,郵費的狀態,訂單的支付方式,訂單的狀態)
- 訂單商品中保存(訂單的id,用戶商品的id,商品的數量,商品的價格)
- 在前端中展現還須要的字段有這次訂單的總金額,以及該訂單中商品的數量,這兩個字段雖然通過表格之間的關聯能夠查詢出來,在這裏能夠將這兩個字段一塊兒定義在訂單的基本信息的表格中,避免在後續查詢訂單的過程當中對數據庫的操做;
具體字段的定義:
from django.db import models from meiduo_mall.utils.models import BaseModel from users.models import User, Address from goods.models import SKU
獲取購物車商品邏輯
- 用戶必須在登陸的狀態下才能夠進入到購物車商品結算的頁面
- 查詢到當前訂單的用戶;
- 在redis數據庫中將當前用戶的購物車中全部商品查詢出來
- 過濾篩選出用戶選中的商品信息的id
- 查詢出當前訂單的運費的
- 將相關信息(例如運費和查詢出來的商品的skus對象)傳遞給序列化器,序列化器將數據從數據庫中序列化後返回給前端
{
"freight":"10.00", "skus":[ { "id":10, "name":"華爲 HUAWEI P10 Plus 6GB+128GB 鑽雕金 移動聯通電信4G手機 雙卡雙待", "default_image_url":"http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRchWAMc8rAARfIK95am88158618", "price":"3788.00", "count":1 }, { "id":16, "name":"華爲 HUAWEI P10 Plus 6GB+128GB 曜石黑 移動聯通電信4G手機 雙卡雙待", "default_image_url":"http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRdPeAXNDMAAYJrpessGQ9777651", "price":"3788.00", "count":1 } ] }
保存訂單
- id的字段定義,默認狀況下采用sql數據庫中的自增的id做爲主鍵,可是在考慮到id的可讀性和擴展性將主鍵設置爲具備特定格式的CharField字段,本身定義的id的格式的;
- 保存訂單的邏輯實現
- 獲取當前下單的用戶
- 獲取用戶的基本信息(用戶的默認地址,用戶選擇的支付方式)
- 建立事物,在如下的任何一個操做不成功的狀況下就會返回到當前這個保存點
- 組織訂單的id
- 建立一個訂單基本信息的對象,進行保存
- redis中取出全部的商品,過濾出用戶選中的商品
- 給訂單的中設計的冗餘的字段賦初值
- 從數據庫中查詢商品信息,並判斷用戶購買的商品庫存狀態和銷量的狀態
- 更新數據庫中庫存和銷量的相關信息。在這個地方用樂觀鎖的方式來判斷在事物保存的過程當中是否有其餘用戶來操做過商品的,若是有則從新返回到事物保存點,沒有則繼續
- 在這裏來查詢用戶的商品信息表中將訂單基本信息的表格中的相關冗餘字段計算出來一塊兒保存進入到數據庫中
class SaveOrderSerializer(serializers.ModelSerializer): """ 用戶支付訂單的建立序列化器""" class Meta: model = OrderInfo fields = ("order_id", "address", "pay_method") read_only_fields = ("order_id",) extra_kwargs = { "address": { "write_only": True, "required": True }, "pay_method": { "write_only": True, "required": True } } def create(self, validated_data): """ 保存訂單序列化器"""
- 數據庫的事物
- Django中對於數據庫的操做,默認在每一次的數據庫操做以後都會自動提交
- 在Django中能夠經過django.db.transaction模塊提供的atomic來定義一個事務,atomic提供兩種用法
- 使用方法一:
from django.db import transaction @transaction.atomic def viewfunc(request):
from django.db import transaction
支付模塊:
import os from alipay import AliPay from django.conf import settings from django.shortcuts import render
-
數據加密的過程
- 在雙方進行通訊的過程當中,若A要給B發送消息
- 互相交換公鑰密碼
- 在數據包的發送過程當中,A先用本身的私鑰加密(保證數據的安全的,至少不會明文顯示)。
- 再用B交給A的公鑰進行加密(只有B有本身的私鑰才能夠打開最外層的包裹信息)