DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • BBS

  • 订单平台

    • 单点知识
      • 需求
      • 发短信
      • 菜单设计思路
        • 方式一: HTML模版
        • 方式二: 配置文件
        • 大致思路
        • 需解决问题
        • 方式三: 数据库
        • 大致思路
        • 具体分析
        • 扩展: 多级评论
      • 权限设计思路
        • 文件的方式
        • 数据库的方式
      • 队列
      • Ajax
      • 创建项目
    • 表结构
    • 用户名登陆
    • 短信登陆
    • 菜单和权限
    • 级别管理
    • 客户管理
    • 分页和搜索
    • 价格策略
    • 交易中心
    • message组件
    • 我的交易列表
    • worker
    • 部署之代码同步
    • 部署之线上运行
    • 张sir的部署
  • CRM

  • flask+layui

  • django+layui

  • 供应链

  • 实战
  • 订单平台
DC
2023-12-01
目录

单点知识

# 需求

需求陈列出来后, 先提取出单点功能, 先实现单点功能!!别一上来就开发.

- 认证模块, 用户名密码 或 手机短信登录(60s有效).

- 角色管理, 不同角色具有不同权限 和 展示不同菜单.
  管理员,充值
  客户,下单
  
- 客户管理, 除了基本的增删改查以外,支持对客户可以分级,不同级别后续下单折扣不同.

- 交易中心
  - 管理员可以给客户余额充值/扣费
  - 客户可以下单/撤单
  - 生成交易记录
  - 对订单进行多维度搜索,例如:客户姓名、订单号.

- worker,去执行订单并更新订单状态.
  worker简单理解就是用python写的一个脚本,它获取到任务后,可以在内部开一些线程池帮助我们并发的进行操作.
  Ps: 可以将py脚本文件打包成exe文件,点击运行.
      脚本文件部署后,还可以进行监控,可以进行重启等操作.
  1> 去redis中取任务
  2> 将该任务/订单在数据库中的状态修改为执行中
  3> 获取任务详细: 比如,将某个视频的播放量刷到10000
  4> 利用线程池或协程实现
  5> 更新订单状态为已完成
  from concurrent.futures import ThreadPoolExecutor
  def task(video_url):
      # 根据视频地址实现刷视频
      pass 
  pool = ThreadPoolExecutor(50)  # 最多有50个线程
  for i in range(10000):  # 将10000个任务交给线程池
      pool.submit(task, "视频地址")  # 执行任务
  pool.shutdown()  # 卡住,等待所有的任务执行完毕.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

[订单系统(刷播放量的)] 先理清楚大体的业务功能!

2、3个角色 + 4个业务模块/20来个路由 的小项目, 可以考虑 基于“配置文件”来实现权限的控制!!!

客户登陆后 客户在平台下单,后台自动执行下单的任务.

image-20230420152207380

管理员登陆后

image-20230420153343099

两角色: 管理员、客户
登陆模块: 用户名密码登陆、短信验证码登陆
角色管理: 不同角色具有不同权限 和 展示不同菜单
管理员:
   级别、客户、价格策略 的CURD.
   管理员可以对客户进行充值或扣款,会产生相应的交易记录.
   管理员可以对客户重置密码.
   管理员可以查看所有的交易记录(可组合搜索)
   管理员可查看所有的订单信息
客户:
   可下单
   可查看自己所有的订单记录
   可查看自己所有的交易记录
1
2
3
4
5
6
7
8
9
10
11
12
13

# 发短信

API服务: 比较繁琐, 得先处理签名和加密等..然后调用接口传入相应的参数.
SDK服务: 已经封装好并在pypi里发布了,直接install就可以使用!!

pip install tencentcloud-sdk-python

from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.sms.v20210111 import sms_client, models

cred = credential.Credential("AKIDVqukLfiV1HGyPZT6SYNvCCwPbXCbjaATABC", "3qMbAWcS1JK4YlhMltlNinb8PP8alfJgABC")

client = sms_client.SmsClient(cred, "ap-guangzhou")

req = models.SendSmsRequest()

req.SmsSdkAppId = "1400804452"
req.SignName = "OnePicec公众号"
req.TemplateId = "1695872"
req.TemplateParamSet = ["688688", '5']
req.PhoneNumberSet = ["+8617380646918"]

resp = client.SendSms(req)

# 输出json格式的字符串回包
print(resp.to_json_string(indent=2))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 菜单设计思路

需求: 不同的用户登陆, 看到不同的菜单.
理性分析: 若角色很少, 就没必要使用方式三(使用数据库,去数据库中查.)

# 方式一: HTML模版

页面写死HTML模版
角色一多, 配置起来也些许麻烦,

<body>
{% if role == "管理员" %}
    <a href="/xxx/xx/">级别列表</a>
    <a href="/xxx/xx/">客户列表</a>
    <a href="/xxx/xx/">价格策略</a>
    <a href="/xxx/xx/">交易策略</a>
    {# ... ... #}
{% else %}
    <a href="/xxx/xx/">价格策略</a>
    <a href="/xxx/xx/">交易策略</a>
{% endif %}
</body>
1
2
3
4
5
6
7
8
9
10
11
12

# 方式二: 配置文件

菜单放到配置文件中
相比于方式一, 对菜单的修改无需动html的内容.. 直接修改菜单的内容.
但角色一多, 配置起来也些许麻烦,

# 大致思路

settings文件中配置

# settings.py

ADMIN = [
    {"title":"级别列表", "url":"..." },
    {"title":"客户列表", "url":"..." },
    {"title":"价格策略", "url":"..." },
    {"title":"交易策略", "url":"..." },
]

USER = [
    {"title":"价格策略", "url":"..." },
    {"title":"交易策略", "url":"..." },
]
1
2
3
4
5
6
7
8
9
10
11
12
13

在模版中的呈现, 当然可以使用自定义的模版 inclusion_tag !!

<body>
{% if 角色 == "管理员" %}
    {% for item in ADMIN %}
        <a href="{{ item.url }}">{{ item.title }}</a>
    {% endfor %}
{% else %}
    {% for item in USER %}
        <a href="{{ item.url }}">{{ item.title }}</a>
    {% endfor %}
{% endif %}
</body>
1
2
3
4
5
6
7
8
9
10
11
# 需解决问题

问题一: 需要显示几个级别的菜单
问题二: 菜单默认选中和展开问题
问题三: 路径导航问题.

# -- 一级菜单
ADMIN = [
    {"title":"级别列表", "url":"..." },
    {"title":"客户列表", "url":"..." },
    {"title":"价格策略", "url":"..." },
    {"title":"交易策略", "url":"..." },
]


# -- 多级菜单
"""
客户管理
   级别列表
      添加
      编辑
      删除
   客户列表
订单管理
   价格策略
   交易策略
"""
ADMIN = [
    {
        "title":"客户管理", 
        "children":[
            # 若url比较长,可以为其设置个name!通过name反向生成即可.
            {
                "title":"级别列表",
                "url":"...", 
                "name":"level_list",
                "children":[
                    {"title":"添加","url":"..."},
                    {"title":"编辑","url":"..."},
                    {"title":"删除","url":"..."},
                ],
            },
            {"title":"客户列表","url":"..."},
        ]
    },
    {
        "title":"订单管理", 
        "children":[
            {"title":"价格策略", "url":"..." },
            {"title":"交易策略", "url":"..." },
        ]
    },
]


# -- 菜单选中和展开问题
1> 获取当前用户请求的URL (eg: pricepolicy/list/ 或 通过url对应的name反向生成)
2> pricepolicy/list/ 匹配 ADMIN中的URL --> 将其在页面上默认选中


# -- 路径导航问题
1> 获取当前用户请求的URL
2> 获取上级,展示导航信息
3> 设置菜单与下级关系
   像添加删除编辑,一般都不挂到菜单展示的地方,而是挂到某个菜单下.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 方式三: 数据库

将菜单 + 角色写入数据库中.
动态分配角色, 比较方便.

# 大致思路
| id   | title    | url  |
| ---- | -------- | ---- |
| 1    | 级别列表 | ...  |
| 2    | 客户列表 | ...  |
| 3    | 价格策略 | ...  |
| 4    | ...... | ...  |


| id   | 角色   |
| ---- | ------ |
| 1    | 管理员 |
| 2    | 用户   |


| id   | role_id | menu_id |
| ---- | ------- | ------- |
| 1    | 1       | 1       |
| 2    | 1       | 2       |
| 3    | 2       | 2       |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在页面展示数据库
1> 连表查询特定角色关联的所有的菜单
2> 在页面上进行展示

# 具体分析

菜单的多级关系 在数据库中如何实现呢? -- 自关联

id title url parent_id
1 客户管理 null null
2 级别列表 ... 1
3 客户列表 ... 1
4 订单管理 null null
5 价格策略 ... 4
6 交易策略 ... 4
7 添加 ... 2
8 编辑 ... 2
9 删除 ... 2
"""
客户管理
   级别列表
      添加
      编辑
      删除
   客户列表
订单管理
   价格策略
   交易策略
"""
客户管理是没有url的,因为点击它只是展开,不会跳转.它属于一级菜单,所以parent_id也为null.
级别列表的parent_id为1,表明它的上一级是客户管理.
添加、编辑、删除的parent_id为2,表明它的上一级都是级别列表.

获取一级菜单 --> 找表中,parent_id等于null的.
获取某个二级菜单(比如客户管理的) --> 找表中,parent_id等于1的.
获取某个三级菜单(比如级别列表的) --> 找表中,parent_id等于2的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 扩展: 多级评论

平台的多级评论 - 自关联

id content root_id parent_id depth
1 优秀 null null 0
2 不咋样 null null 0
3 确实 1 1 1
4 哈哈 1 1 1
5 你说的都对 1 3 2
"""
优秀
	确实
		你说的都对
	哈哈
不咋样
"""
根据生活经验,不难知道,有些评论系统默认只展示二级评论,三级四级的评论要点击才会显示.
若我只想展示一级评论,就在表中找depth等于0的记录.
root_id表明根评论,parent_id表明父评论
比如: `优秀`,`不咋样`这两条评论是根评论; `确实`,`哈哈`这两条评论是`优秀`的子评论; 
     `你说的都对`的父评论是`确实`,其根评论是`优秀`
    
拿`优秀`相关联的所有评论的时候,只需要查root_id等于1的.给他回复的,以及子孙回复的,通过它都能拿到!
只拿`优秀`的子评论的时候,只需要查parent_id等于1的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 权限设计思路

菜单是用户登陆后在左侧菜单展现出来的东西. 不仅如此, 在用户访问url的时候应该做上权限的校验!!
即 权限的判断时,要考虑: 通过正常的点击菜单跳转的url 、在浏览器上非法输入的url..

# 文件的方式

权限就是url,若需要频繁的判断 某个url是否存在/是否有该权限, 用字典集合比列表好,字典和集合用了哈希,列表是挨个比较的.推荐用字典.
比如:
   admin_permissions = {
       "/xxx/xx/":{url相关的其它信息},
       "/level/edit/<int:id>/":{...},
   }
Q:思考一个问题,若访问的是动态路由/level/edit/4/, 那么"/level/edit/4/" in admin_permissions 的值为False.
A:那如何是好? 用路由的name!!反向生成url.  name代表的是一个url的唯一标识!!
  注意!在中间件里根据url的name进行权限的校验,要清楚中间件生命周期的流程
      process_request -- 路由匹配 -- process_view -- 视图 -- process_response
      在路由匹配成功后,才能拿到url相关信息(name、namespace等),路由没匹配成功,name是获取不到的,
      路由匹配成功后,通过`request.resolver_match`获取. So,在中间件的process_view中进行校验!!
即:
   admin_permissions = {
       "level_list":{url相关的其它信息},
       "level_edit":{...},
       "level_add":{...},
       "level_del":{...},
   }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

优化一个细节 - 方式二,用文件的形式

★ 优化一个细节,在菜单里只展示菜单,将在菜单下的添加删除编辑放到权限部分,并添加一个关联关系即可.
   管理员登陆后,展示菜单就读取ADMIN这个列表即可
   点击菜单中的‘级别列表’,发送了请求,会先经过中间件,在中间件里进行权限校验
      路由匹配成功 -- 得到路由对象的name -- 该name是否在admin_permissions字典中
      若在,它是否有上级?比如 通过 /level/edit/4/ 路由匹配成功 得到 name为level_edit,其上级为level_list
   再去菜单中找,若菜单中某个name为level_list,那么该菜单应该被默认选中!!

# -- 菜单 (只做菜单展示)
ADMIN = [
    {
        "title":"客户管理", 
        "children":[
            {
                "title":"级别列表",
                "url":"...",  # ▲ url都可以不写,有name可以反向生成.
                "name":"level_list",
                # "children":[
                    # {"title":"添加","url":"..."},
                    # {"title":"编辑","url":"..."},
                    # {"title":"删除","url":"..."},
                # ],
            },
            {"title":"客户列表", "url":"...", "name":"user_list"},
        ]
    },
    {
        "title":"订单管理", 
        "children":[
            {"title":"价格策略", "url":"...", "name":"price"},
            {"title":"交易策略", "url":"...", "name":"transaction" },
        ]
    },
]
USER = [...]
# -- 权限 (只做权限的校验)
admin_permissions = {
    "level_list":{...},
    "level_edit":{...,'parent':'level_list'},
    "level_add":{...,'parent':'level_list'},
    "level_del":{...,'parent':'level_list'},
    "user_list":{...},
    "price":{...},
    "transaction":{...},
}
user_permissions = {...}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 数据库的方式

>>>
客户管理
   级别列表
      # 添加
      # 编辑
      # 删除
   客户列表
订单管理
   价格策略
   交易策略
>>>
★
这里的业务规定用户要么是管理员要么是普通用户.所以不存在多个角色.
扩展,若一个用户对应多个角色,那么还得创建一个 用户_角色的关系表!!

★
若让菜单表再跟角色表进行关联,角色一多,记录也多起来,而且菜单角色表有点像角色权限表.. So,在权限表里加了一个is_menu字段!!
逻辑:(假设是管理员登陆)
  登陆后,根据角色权限关联表得到权限
     eg:类似于得到文件方式的admin_permissions字典.
  在权限表里,有些权限是菜单、有些不是菜单.拿到是菜单的权限去菜单表里匹配,获取到自己以及上级菜单.
     eg:类似于得到文件方式的ADMIN这个列表.
  So,在用户登陆时,需进行表查询,得到菜单字典和权限字典.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

image-20230321124506176


# 队列

可作队列的服务 rabbitMQ、kafka、redis, 该项目选择用redis作队列服务.

"""
mac
"""
  1> 在官方下载redis的源码包
  2> 解压、编译 
     tar zxvf redis-5.0.14.tar.gz
     sudo make test
     sudo make install
  3> 修改redis的配置文件
  4> 启动
One_Piece@DC的MacBook redis-5.0.14 % pwd
/usr/local/redis-5.0.14
# -- 修改两处地方,   bind 0.0.0.0     requirepass qwe123
# ?requirepass直接跳转到requirepass的地方.
One_Piece@DC的MacBook redis-5.0.14 % vim redis.conf
# -- 在本地启动redis
One_Piece@DC的MacBook redis-5.0.14 % ./src/redis-server redis.conf



"""
使用python远程操作redis -- 短信验证读秒
pip install redis 扩展,还可以使用Django-redis模块!
"""
import redis

conn = redis.Redis(host='127.0.0.1', port=6379, password='qwe123', encoding='utf-8')
conn.set('123456', 'hello', ex=10)  # 设置了个超时时间,10s后该键值对就取不到啦!
value = conn.get('123456')
print(value)  # b'hello'



"""
使用redis构建队列 -- 存取任务
运用redis构建一个先进先出的列表(队列), worker从里面取任务, 若有, 立刻取走, 若没有, 就等10s, 10s内没有就将连接断开.. 
再次发送一个请求, 重新连接.. 重新等待10s. 即没有数据的话, worker每10s向redis发送一个连接请求.. 
而不是队列中无数据, worker保持该连接一直等待队列中有数据. 减轻程序和redis的压力.
"""
import redis

conn = redis.Redis(host='127.0.0.1', port=6379, password='qwe123', encoding='utf-8')
# 往名为my_queue的队列里放了两个数据 'root'和'good'
conn.lpush('my_queue', 'root')
conn.lpush('my_queue', 'good')
# 依次取出两个值后,阻塞5秒,得到一个返回值None
# (b'my_queue', b'root')
# (b'my_queue', b'good')
# None
while True:
    v = conn.brpop('my_queue', timeout=5)
    print(v)
    if not v:
        break
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# Ajax

image-20230323115711823

-- form表单形式提交,解决csrf 403的报错.
   ★ 在表单中加入 {% csrf_token %} 
   会自动生成一个隐藏的input标签.name值为csrfmiddlewaretoken,value值为随机的一串字符串.
   点击submit按钮,会提交到后端.
-- Ajax形式提交,解决csrf403的报错.
1
2
3
4
5

# 创建项目

1> 创建虚拟环境的python项目
2> pip install django==3.2
3> django-admin startproject order .
4> python manage.py startapp web
   记得注册app
5> pip install pymysql
   """
   import pymysql
   pymysql.install_as_MySQLdb()
   """
6> pip install tencentcloud-sdk-python
7> 纯净版Django
8> 连接数据库的配置
   """
   DATABASES = {
       'default': {
           'ENGINE': 'django.db.backends.mysql',
           'NAME': 'order',
           'USER': 'root',
           'PASSWORD': '123456',
           'HOST': '127.0.0.1',
           'PORT': '3306',
           'CHARSET': 'utf8'
       },
   }
   """
9> pip install django-redis
   session的存储位置: session存储到redis缓存中
   """
   CACHES = {
       "default": {
           "BACKEND": "django_redis.cache.RedisCache",
           "LOCATION": "redis://127.0.0.1:6379",
           "OPTIONS": {
               "CLIENT_CLASS": "django_redis.client.DefaultClient",
               "CONNECTION_POOL_KWARGS": {"max_connections": 100,"encoding":"utf-8"},
               "PASSWORD": "qwe123",
           }
       }
   }

   SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
   SESSION_CACHE_ALIAS = 'default'
   SESSION_COOKIE_NAME = "sid"
   SESSION_COOKIE_PATH = "/"
   SESSION_COOKIE_DOMAIN = None
   SESSION_COOKIE_SECURE = False
   SESSION_COOKIE_HTTPONLY = True
   SESSION_COOKIE_AGE = 1209600
   SESSION_EXPIRE_AT_BROWSER_CLOSE = False
   SESSION_SAVE_EVERY_REQUEST = True
   """
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

评论
表结构

← 评论 表结构→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式