# meiduo_project **Repository Path**: mosson/meiduo_project ## Basic Information - **Project Name**: meiduo_project - **Description**: 仿淘宝 美多商城,前端使用 vue 构建,后端使用 django - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2024-08-16 - **Last Updated**: 2024-08-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README >@[TOC] --- # 💘 相关说明 本项目采用的为前后端分离,前端使用的框架为Vue2,使用的组件库为Vant2和Element,前端使用的语言为HTML5+CSS3+JavaScript,后端使用的语言为Python,后端使用的框架为Django,使用的数据库为MySQL,使用的缓存数据库为Redis。 项目的前后端源码和数据库SQL脚本源码等我码完再加上。 本项目会使用到的配置基本就是本文写的了,剩下的就是功能的实现和前后端数据交互。这篇文章没写完,应该也不会继续写了。~~(好吧,其实就是不想写了)~~ 注意点: ![在这里插入图片描述](https://img-blog.csdnimg.cn/cec2f3e910e9479ba75dd2d42333fa90.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/4281ae54160a408f8546cf6820f8a1a4.png) --- # 💘 美多商城前台前端项目创建与配置 ## 💖 项目的创建 美多商城前端使用的框架为Vue2.x,前端搭建的为移动端页面。 创建前端项目的脚手架为vue-cli。 ```sql vue create meiduo_move ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/bd4c2ecea8c14f21a76d3645017e49fa.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/c2ae427b61fd4956a05f15acbad41221.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/53143305c0b64a969c0d2ebdfdf98af8.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/67fb8a7b356b4927a9f7c947849ccd20.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/4ae6c304f76d42d68afc807841660dcb.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/75daf1b9b7b64cf19adfc7da6ae7550a.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/5d05b4b834f347be8e6201477419d459.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/37675233e00b4050843efcbc3fd04dfd.png) vscode打开项目,删除不需要的文件和代码,并运行测试: ![在这里插入图片描述](https://img-blog.csdnimg.cn/068b388ec73b4ca089b40f8ae31710e5.png) main.js ```js import Vue from 'vue' import App from './App.vue' import router from './router' Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app') ``` App.vue ```html ``` router/index.js ```js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ ] const router = new VueRouter({ routes }) export default router ``` 运行项目测试: ```js npm run serve ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/05d5c37b8522492fabb81d951c06cb53.png) ## 💖 配置UI组件库 前台前端页面使用UI组件库为Vant UI,Vant的版本为Vant2。 >[Vant2:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/quickstart](https://vant-contrib.gitee.io/vant/v2/#/zh-CN/quickstart) ### 💝 安装 项目中安装Vant2组件库: ```html npm i vant@latest-v2 -S ``` 安装插件,Vant2自动按需引入: ```html npm i babel-plugin-import -D ``` 在 babel.config.js 文件中增加配置项 plugins: ```js plugins: [ ['import', { libraryName: 'vant', libraryDirectory: 'es', style: true }, 'vant'] ] ``` ```javascript module.exports = { presets: [ '@vue/cli-plugin-babel/preset' ], plugins: [ ['import', { libraryName: 'vant', libraryDirectory: 'es', style: true }, 'vant'] ] } ``` 停止项目重新运行: ```js npm run serve ``` ### 💝 全局引入与全局注册 在 src 目录下新建 lib 文件夹用于存放组件库引入的代码文件: ![在这里插入图片描述](https://img-blog.csdnimg.cn/93338429a8cd440c995f55242ff82131.png) 在lib文件夹中新建vantUI.js文件用于vantUI组件的全局引入与全局注册: ![在这里插入图片描述](https://img-blog.csdnimg.cn/75228f6049fc4220be58a3f982df03c4.png) 在vant_ui.js文件中全局按需引入组件,并注册为全局组件: ```javascript import Vue from 'vue' // Vant组件的引入 import { Button } from 'vant' // Vant组件注册为全局组件 Vue.use(Button) ``` 在main.js文件中导入vant_ui.js文件: ```javascript import Vue from 'vue' import App from './App.vue' import router from './router' import '@/lib/vantUI.js' // 引入全局导入与全局注册vant组件的文件 Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app') ``` 在App.vue文件中测试: ```html ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/d08f0a466407465598eb3d3bcf02045e.png) ### 💝 局部引入与局部注册 局部按需引入组件: ```html ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/62bb320fe00248c094cfc72f4343bcf3.png) ### 💝 定制主题 配置按需引入vant组件库的样式,在原先的babel.config.js配置文件中的plugins配置项中修改style配置: ```javascript // 指定样式路径 style: (name) => `${name}/style/less`, ``` ```javascript module.exports = { presets: [ '@vue/cli-plugin-babel/preset' ], plugins: [ ['import', { libraryName: 'vant', libraryDirectory: 'es', // 指定样式路径 style: (name) => `${name}/style/less`, }, 'vant'] ] } ``` 在src目录下新建less文件夹,用于存放自定义样式的代码文件: ![在这里插入图片描述](https://img-blog.csdnimg.cn/15801ba0a49a4fed9089044790c7c38c.png) 在less文件夹中,新建vant_theme.less文件,用于自定义组件样式: ![在这里插入图片描述](https://img-blog.csdnimg.cn/ea423ac446144b45b800c4a683abb042.png) vue-cli 搭建的项目,在 vue.config.js 中进行如下配置: ```javascript const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, css: { loaderOptions: { less: { // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。 lessOptions: { modifyVars: { // 通过 less 文件覆盖(文件路径为绝对路径) hack: `true; @import "@/less/vant_theme.less";`, }, }, }, }, }, }) ``` 重新启动运行项目,接下来即可在vantTheme.less文件中自定义组件的样式。 ## 💖 配置axios ### 💝 axios的配置 安装axios ```javascript npm i axios ``` 在lib文件夹下新建axios.js文件用于配置和挂载axios ![在这里插入图片描述](https://img-blog.csdnimg.cn/5fadddf7bf1e4738a7487ad78b907b76.png) 在axios.js文件夹中进行如下配置: ```javascript import Vue from "vue" import axios from "axios" const req = axios.create({ baseURL: 'http://127.0.0.1:8000/', // 配置baseURL withCredentials: true, // axios 发送请求时允许携带cookie }) Vue.prototype.req = req // 将req挂载到Vue的原型对象上 ``` 在main.js文件中引入axios.js ```javascript import Vue from 'vue' import App from './App.vue' import router from './router' import '@/lib/vant_ui.js' // 引入全局导入与全局注册vant组件的文件 import '@/constants/user_constants.js' // 引入与用户相关的常量并挂载到Vue原型上 import '@/lib/axios.js' // 引入axios配置 ``` ### 💝 配置代理 配置代理,实现可以获取到服务器响应回来的set-cookie. 在vue.config.js文件的配置对象中新增devServer配置项: ```javascript const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, css: { loaderOptions: { less: { // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。 lessOptions: { modifyVars: { // 通过 less 文件覆盖(文件路径为绝对路径) hack: `true; @import "@/less/vant_theme.less";`, }, }, }, }, }, devServer: { // 配置代理服务器 proxy: { '/api': { // '/api' 防止与本地资源冲突 target: 'http://localhost:8000/', // 代理服务器向http://127.0.0.1:8000/请求资源 pathRewrite: { '/api': '' }, // 重写请求路径 ws: true, // 支持websocket changeOrigin: true, // 服务器收到的请求头中的host与服务器一样 } } } }) ``` 修改axios请求的根地址为与页面运行的地址一样: ```javascript import Vue from "vue" import axios from "axios" const req = axios.create({ // 记得 api/ baseURL: 'http://localhost:8080/api/', // 配置baseURL,向代理服务器请求 withCredentials: true, // axios 发送请求时允许携带cookie }) Vue.prototype.req = req // 将req挂载到Vue的原型对象上 ``` 重启运行项目。 ## 💖 常量声明挂载文件 在src目录下新建constants文件夹用于存放常量声明挂载的文件。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3c83f32359e84b6ba771f99552a6cd9a.png) ### 💝 用户相关 在constants文件夹下新建user_constants.js文件夹用于与用户相关的常量的声明和挂载。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/784ead97b7e64c28a9b70e21be4f354d.png) ```javascript import Vue from "vue" Vue.prototype.userConstants = { } ``` 在main.js文件中导入该文件,执行并向Vue原型上挂载。 ```javascript import Vue from 'vue' import App from './App.vue' import router from './router' import '@/lib/vant_ui.js' // 引入全局导入与全局注册vant组件的文件 import '@/constants/user_constants.js' // 引入与用户相关的常量并挂载到Vue原型上 Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app') ``` ## 💖 工具文件 在src目录下新建utils文件夹用于存放工具文件,在utils中新建utils.js文件用于工具方法的声明。 --- # 💘 美多商城前台前端路由说明 - /container: - 用于放置主页、分类界面、购物车界面、用户界面组件的容器的路由 - 路由命名:container - 对应组件:Container.vue - /home: - /container 下的子路由 - 完整路由:/container/home - 主页界面路由 - 路由命名:home - 对应组件:Home.vue - /category: - /container 下的子路由 - 完整路由:/container/category - 分类界面路由 - 路由命名:category - 对应组件:Category.vue - /cart: - /container 下的子路由 - 完整路由:/container/cart - 购物车界面路由 - 路由命名:cart - 对应组件:Cart - /user: - /container 下的子路由 - 完整路由:/container/user - 用户界面路由 - 路由命名:user - 对应组件:User.vue - user/receive/address: - 用户收货地址界面路由 - 路由命名:receiveAddress - 对应组件:UserReceiveAddress - user/userdetailinfo: - 用户详细信息界面路由 - 路由命名:userdetailinfo - 对应组件:UserDetailInfo - /user/setting: - 用户设置界面路由 - 路由命名:setting - 对应组件:Setting - /user/changepassword: - 修改界面路由 - 路由命名: changepassword - 对应组件:ChangePassword - /login: - 登录界面路由 - 路由命名:login - 对应组件:Login.vue - /register: - 注册界面路由 - 路由命名:register - 对应组件:Register.vue --- # 💘 美多商城前台前端Vue挂载自定义变量说明 - userConstants - 对象类型 - 该对象上存放与用户相关的常量 - 声明文件:src/constants/user_constants.js --- # 💘 美多商城前台后端项目创建与配置 美多商城前台后端使用的语言为Python,使用的框架为Django。由于采用的为前后端分离的方式,所以这里不使用Django的模板;同时由于本次主要用户数据库课设,而Django对于数据库的一些功能,是在代码逻辑层面进行实现的,即对于数据库中数据表的一些设置不会在对应的数据库中有很好的体现,如设置外键时表的级联删除,Django实现该功能就是在代码逻辑层面进行实现的,在数据库中数据表的设置中显示的为no action。 >使用Python中的Django是由于Python的Web框架目前只学了Django。(~~交作业要紧~~ ) ## 💖 项目的创建 >使用的虚拟环境工具为virtualenvwrapper。 创建用于本次项目的虚拟环境: ```javascript mkvirtualenv -p D:\python3.9.7\python.exe meiduo_environment ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/f2685106fbb0464e9dc165d29a0c5dfb.png) 选择本次项目后端存放的文件夹路径,进入新创建的虚拟环境: ```javascript workon meiduo_environment ``` 在新创建的虚拟环境中安装Django: ```javascript pip install django -i https://pypi.tuna.tsinghua.edu.cn/simple ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/7ac7c21d7a96497ba44890b6924800d3.png) 进行本次美多商城前台后端项目的创建: ```javascript django-admin startproject meiduo_move_server ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/79412db024d840db888e01ecb6c2c0af.png) 在pycharm中打开项目,试运行: ![在这里插入图片描述](https://img-blog.csdnimg.cn/1c67565a28b44a8184cf9580331c575c.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/47ee62b0e5dd431f91fd984f4bf09d43.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/e0526dc8fa4b4d8c95da454ea44ff7f2.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/d4f40f4463454269aaafae59697e7f00.png) 至此项目创建成功。 ## 💖 去除不需要使用的项目默认子应用与中间件 由于本项目采用前后端分离的方式,同时不使用Django的模型与模板,所以将默认子应用admin(内置的后台管理系统)、auth(内置的用户认证系统)、contenttype(用于记录项目中model元数据)进行注释。 ```py INSTALLED_APPS = [ # 'django.contrib.admin', # 'django.contrib.auth', # 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] ``` 由于不需要使用项目内置的用户认证系统,所以将AuthenticationMiddleware中间件进行注释。 ```py MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', # 注释避免出现403 (Forbidden) # 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ``` ## 💖 配置MySQL数据库 安装PyMySQL: ```javascript pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple ``` 由于使用的是原生的SQL语句脚本,所以不进行后序的配置。 python使用原生的SQL操作数据库的代码写在子应用的models.py文件中。 ## 💖 配置Redis数据库 安装 django-redis: ```javascript pip install django-redis -i https://pypi.tuna.tsinghua.edu.cn/simple ``` 在Django项目的配置文件中,新增CACHES配置项,用于Redis数据库的配置: >与DATABASES配置项平级 ```py DATABASES = { ...... } # 配置redis数据库 CACHES = { "default": { # 默认使用的redis的0号数据库 "BACKEND": "django_redis.cache.RedisCache", # 使用redis作为缓存 "LOCATION": "redis://127.0.0.1:6379/0", # redis 数据库所在的主机地址 端口号 选择第几个数据库 "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": "123123", # 访问redis数据库的密码 } }, "user": { # 与用户相关的数据存放在Redis的1号数据库 "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": "123123", } }, } ``` ## 💖 配置跨域 安装django-cors-headers: ```javascript pip install django-cors-headers ``` 在项目的配置文件中注册跨域子应用corsheaders ```py INSTALLED_APPS = [ # 'django.contrib.admin', # 'django.contrib.auth', # 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', # 处理跨域子应用 'user', ] ``` 在项目的配置文件中新增处理跨域的中间件: ```py MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', # 处理跨域的中间件 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ``` 在配置文件中新增如下配置项: ```py CORS_ALLOW_CREDENTIALS = True # 设置请求是否允许携带Cookie信息 默认为False CORS_ORIGIN_ALLOW_ALL = True # 允许所有的域名发送请求 CORS_ORIGIN_WHITELIST = () # 设置可以发送请求的域名 CORS_ALLOW_METHODS = ( # 允许的请求方式 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( # 允许的请求头 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', ) ``` ## 💖 子应用的创建 ### 💝 user user子应用用于处理和用户相关的请求并向浏览器返回相应的数据响应。 在项目的根目录下创建user子应用: ```javascript python manage.py startapp user ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/98f5f8cfe5834b208c121d2de2b306df.png) 在项目配置文件的INSTALLED_APPS配置项中注册user子应用: ```py INSTALLED_APPS = [ # 'django.contrib.admin', # 'django.contrib.auth', # 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'user' ] ``` ## 💖 常量声明文件 在项目配置文件所在的文件夹中新建project_constants.py文件用于与本项目相关的常量的声明。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/cd57154c276646d3b84b0e790e6ea6fe.png) 在user子应用的目录下新建user_constants.py文件用于与用户相关的常量的声明。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/7728258dda224d1d9b9cce522b865d1e.png) ## 💖 工具文件 在子应用user目录下新建utils包用于存放和用户相关的工具文件. ![在这里插入图片描述](https://img-blog.csdnimg.cn/b94e83fb0e994e2aa4153054c902fa78.png) ### 💝 发送短信验证码的工具文件 在utils文件夹中新建sms_utils.py文件,用于声明和短信相关的工具类或方法。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/75b9cb2ddc9e4f3ab1517b665a94de11.png) 安装容联云短信API平台的SDK ```javascript pip install ronglian-sms-sdk -i https://pypi.tuna.tsinghua.edu.cn/simple ``` 在sms_utils.py文件中,使用如下代码: >记得修改accId accToken appId ```py from ronglian_sms_sdk import SmsSDK import json # accId = '容联云通讯分配的主账号ID' accId = '...' # accToken = '容联云通讯分配的主账号TOKEN' accToken = '...' # appId = '容联云通讯分配的应用ID' appId = '...' class SendSmsVerificationCode: """发送短信验证码的单例类""" def __new__(cls, *args, **kwargs): """ 发送短信验证码单例类的初始化方法 :return: 返回一个发送短信验证码的对象 """ # 判断类中发送短信验证码的对象 _instance 是否已经存在 # 如果不存在, 创建一个发送短信验证码的对象, 并将其作为类属性 if not hasattr(cls, '_instance'): # 创建发送短信验证码的对象 cls._instance = super(SendSmsVerificationCode, cls).__new__(cls, *args, **kwargs) # 创建SmsSDK对象作为 _instance 的对象属性 cls._instance.sdk = SmsSDK(accId, accToken, appId) # 如果存在, 返回发送短信验证码的对象 return cls._instance def send_message(self, mobile, datas, tid='1'): """ 发送短信的方法 @params mobile 字符串类型 mobile = '手机号1,手机号2' @params tid tid = '容联云通讯平台创建的模板' 默认模板的编号为1 @params datas 元组类型 第一个参数为验证码 第二个参数为验证码的有效时间(对于短信模板1) :return: 返回发送短信后的响应参数 """ # 发送短信 resp = self.sdk.sendMessage(tid, mobile, datas) print(json.loads(resp), type(json.loads(resp))) return resp # 测试 if __name__ == '__main__': sendSmsVerificationCode1 = SendSmsVerificationCode() sendSmsVerificationCode2 = SendSmsVerificationCode() sendSmsVerificationCode3 = SendSmsVerificationCode() print(sendSmsVerificationCode1) print(sendSmsVerificationCode2) print(sendSmsVerificationCode3) res = sendSmsVerificationCode1.send_message('...', ('123456', 5), '1') print(res) ``` ### 💝 操作数据库的工具文件 在项目配置文件所在的文件目录下新建utils包用于存放和本项目相关的工具文件。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/21877199213e428fbcf4a882d8bb91cb.png) 在utils文件夹下新建mysql_utils.py文件用于声明和操作mysql数据库相关的工具类或方法。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/12cf6528ecec498c990f6c0d1ce4a9ef.png) 在mysql_utils.py文件中写如下用于操作mysql数据库的工具类: ```py import pymysql class MySQLUtils(): """ 操作MySQL数据库的工具类 """ @staticmethod def getConnection(): """ 获取MySQL数据库的连接对象 @return: MySQL数据库的连接对象 """ conn = None try: # 获取MySQL数据库连接对象 conn = pymysql.connect( host='localhost', port=3306, user='root', password='123456', database='meiduo', charset='utf8' ) except Exception as e: print('获取MySQL数据库连接对象失败') print(e) # 返回数据库的连接对象 return conn @staticmethod def update(sql, params=None): """ 更新MySQL数据库中数据的通用方法,包括对数据库中数据的增删改 @param - sql: 需要执行的sql语句 @param - params: 向sql语句中填充的参数,元组类型,默认值为None @return: 返回本次操作对数据库中数据的影响行数 """ conn = MySQLUtils.getConnection() # 获取MySQL的连接对象 cur = None row_count = None try: cur = conn.cursor() # 获取执行sql的游标对象 if params is None: row_count = cur.execute(sql) else: row_count = cur.execute(sql, params) conn.commit() # 提交本次操作 except Exception as e: conn.rollback() # 如果执行SQL语句时出现异常则回滚本次操作 print('执行SQL语句时出现异常') print(e) finally: MySQLUtils.closeResource(conn=conn, cur=cur) # 关闭连接数据库的资源 return row_count @staticmethod def select(sql, params=None): """ 查询MySQL数据库中数据的通用方法 @param - sql: 需要执行的sql语句 @param - params: 向sql语句中填充的参数,元组类型,默认值为None @return: 返回的为字典类型数据,字典中row_count为本次查询结果的行数,data为本次查询结果集(元组类型) """ conn = MySQLUtils.getConnection() # 获取MySQL数据库的连接对象 cur = None row_count = None data = None try: cur = conn.cursor() # 获取执行SQL的游标对象 if params is None: row_count = cur.execute(sql, params) # 执行SQL并获取本次查询结果集的行数 else: row_count = cur.execute(sql, params) # 执行SQL并获取本次查询结果集的行数 data = cur.fetchall() # 获取本次查询结果集 except Exception as e: print('执行SQL语句时出现异常') print(e) finally: MySQLUtils.closeResource(conn=conn, cur=cur) # 返回查询结果集 return { 'row_count': row_count, 'data': data } @staticmethod def closeResource(conn=None, cur=None): """ 关闭与操作MySQL数据库相关的资源对象 @param - conn: MySQL数据库的连接对象 @param - cur: 执行sql语句的游标对象 @return: 无返回值 """ try: if cur is not None: cur.close() if conn is not None: conn.close() except Exception as e: print('关闭MySQL资源对象出错') print(e) if __name__ == '__main__': # sql = "INSERT INTO user_account_info(user_account, user_telephone, user_password, username) VALUES (%s, %s, %s, %s);" # print(MySQLUtils.update(sql, ('123', '123', '123', '123'))) # sql = 'select * from user_account_info' # print(MySQLUtils.select(sql)) pass ``` ### 💝 与请求处理相关的工具文件 在项目配置文件所在目录下的utils包中新建request_utils.py文件用于声明与请求处理相关的工具类和方法。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3a3bc98cdfe7444480bc15f3948aa99c.png) ### 💝 与数据类型转换相关的工具文件 在项目配置文件所在目录下的utils包中新建data_type_change_utils.py文件用于声明与数据类型转换相关的工具类和方法。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/203ceb4223054991b80fe446e6e8ffc9.png) ### 💝 与用户账号相关的工具文件 在子应用user的utils包中新建user_account_utils.py文件用于声明与用户账号相关的工具类和方法。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/822ce563955043c79ca883a4ad8b09b6.png) ### 💝 与日期时间相关的工具文件 ![在这里插入图片描述](https://img-blog.csdnimg.cn/f5671d9048e046d2ab42c502db566b1e.png) ### 💝 与jwt相关的工具文件 安装pyjwt: ```javascript pip install pyjwt ``` 在user/utils包中新建文件: ![在这里插入图片描述](https://img-blog.csdnimg.cn/0101126c55b64bd39d852d9dce9db3f9.png) ```py """ 声明和jwt相关的工具类或方法 """ import jwt import datetime from jwt import exceptions from meiduo_move_server.settings import SECRET_KEY # 获取django自动生成的项目密钥 from user.user_constants import TOKEN_VALID_TIME # token的有效时间 def create_token(userAccount, userPassword): """ 根据用户的账号或手机号和密码生成对应的token @param - userAccount: 用户账号或手机号 @param - userPassword: 用户账号密码 @return: 返回用户账号对应的token """ # 构造header,JWT的元数据 headers = { 'typ': 'jwt', # 令牌类型,JWT 'alg': 'HS256' # 生成签名算法 } # 构造payload,存放实际数据 payload = { 'userAccount': userAccount, # token中包含的数据 'userPassword': userPassword, # token中包含的数据 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=TOKEN_VALID_TIME) # 设置token过期时间 } token = jwt.encode(payload=payload, key=SECRET_KEY, algorithm="HS256", headers=headers) return token def get_payload(token): """ 获取token中的payload,判断token是否有效,如果token有效返回payload中的包含数据,否则返回None @param - token: token @return: 如果token有效返回payload中的包含数据,否则返回None """ try: # 从token中获取payload【校验合法性】 verified_payload = jwt.decode(jwt=token, key=SECRET_KEY, algorithms=['HS256']) # print(verified_payload) return verified_payload except exceptions.ExpiredSignatureError: # print('token已失效') return None except jwt.DecodeError: # print('token认证失败') return None except jwt.InvalidTokenError: # print('非法的token') return None if __name__ == '__main__': token = create_token('15892014635', '11111111') try: # 从token中获取payload【不校验合法性】 verified_payload = jwt.decode(jwt=token, key=SECRET_KEY, algorithms=['HS256']) print(verified_payload) except exceptions.ExpiredSignatureError: print('token已失效') except jwt.DecodeError: print('token认证失败') except jwt.InvalidTokenError: print('非法的token') ``` --- # 💘 美多商城前台后端路由说明(请求路由) - user/ - 处理与用户相关的请求 - 所有处理与用户相关请求的路由均为该路由的子路由 - user/register/ - 处理新用户的注册 - get: - 向该路由发起get请求用于获取用户注册时所需的短信验证码,发送get请求的同时传递注册用户的手机号,会向该手机号发送短信验证码,同时会向浏览器返回相应的响应数据对象 - 所需参数: - userTelephone:用户手机号 - 响应数据对象中的数据项: - mark:本次处理成功或失败的标记,成功为1,失败为0 - message:本次请求处理相关的信息说明 - post: - 向该路由发起post请求,用于实现新用户的注册,发送post请求的同时需要传递用户手机号、用户账号密码以及短信验证码。 - 所需参数: - userTelephone:用户手机号 - userPassword:用户账号密码 - smsVerificationCode:短信验证码 - 响应数据对象中的数据项: - mark:本次请求处理成功或失败的标记,成功为1,失败为0 - message:本次请求处理相关的信息说明 - user/login/ - 处理用户的登录请求 - get: - 向该路由发送get请求,用于判断当前用户token是否有效,发送请求时需要传递token(保存于localStorage中) - 所需参数: - token - 响应数据对象中的数据项: - mark:本次请求处理成功或失败的标记,成功为1,失败为0 - message:本次请求处理相关的信息说明 - post: - 向该路由发送post请求,用于处理用户的登录请求,发送post请求的同时需要传递用户登录的账号或手机号和用户账号密码,收到请求后会对账号进行验证,向浏览器返回相应的响应对象。 - 所需参数: - userAccount / userTelephone:用户账号或用户手机号 - userPassword:用户账号密码 - 响应数据对象中的数据项: - mark:本次请求处理成功或失败的标记,成功为1,失败为0,失效为2 - message:本次请求处理相关的信息说明 - token:登录的凭证,响应对象的data中,浏览器页面会保存于localStorage中 - user/username/userAccount/ - 向该路由发起get请求,获取当前登录用户的用户名和用户账户以及用户头像的路径地址,发送get请求时需要传递当前用户的登录令牌token(保存于localStorage中) - 所需参数: - token:当前用户的登录令牌token - 响应数据对象中的数据项: - mark:本次请求处理成功或失败的标记,成功为1,失败为0,失效为2 - message:本次请求处理相关的信息说明 - username:当前登录用户的用户名,响应数据对象的data中 - userAccount:当前登录用户的用户账号,响应数据对象的data中 - userHeadPicture:当前登录用户的用户头像的路径地址,响应数据对象的data中 - user/userinfo/ - 向该路由发起get请求,获取当前登录用户的个人信息,发送get请求时需要传递当前用户的登录令牌token(保存于localStorage中),获取的信息用于个人信息页面 - 所需参数: - token:当前用户的登录令牌token - 响应数据对象中的数据项: - mark:本次请求处理成功或失败的标记,成功为1,失败为0,失效为2 - message:本次请求处理相关的信息说明 - userHeadPicture:当前登录用户的用户头像的路径地址,响应数据对象的data中 - userAccount:当前登录用户的用户账号,响应数据对象的data中 - username:当前登录用户的用户名,响应数据对象的data中 - userRealName:用户真实新名,响应数据对象的data中 - userGender:用户性别,响应数据对象的data中 - userIdCardNumber:用户身份证号,响应数据对象的data中 - userTelephone:用户手机号,响应数据对象的data中 - userEmail:用户邮箱,响应数据对象的data中 - user/change/userinfo/ - 向该路由发起post请求,修改当前登录用户的指定用户信息,发送post请求时需要传递当前用户的登录令牌token(保存于localStorage中)和修改的信息项(变量名)与修改后的值 - 所需参数: - token:当前用户的登录令牌 - field_name:当前要修改的信息项名 - param:要修改信息项的修改后的值 - 响应数据对象中的数据项: - mark:本次请求处理成功或失败的标记,成功为1,失败为0,失效为2 - message:本次请求处理相关的信息说明 --- # 💘 美多商城前台变量与常量说明 >常量均通过对象挂载到Vue原型对象上 ## 💖 与用户相关 ### 💝 变量 - userAccount - 用户账号 - 字符串类型 - 用户账号的生成规则:mdyyyymmddxxxx,其中,md为每个账号的固定开头(meiduo拼音的缩写),yyyy为注册时间的年份,mm为注册时间的月份,dd为注册时间的天,xxxx为注册用户手机号的后四位。 - 规定一个手机号只能注册一个账号。 - userTelephone - 用户手机号 - 字符串类型 - userPassword - 用户账号密码 - 字符串类型 - userHeadPicture - 用户头像路径地址 - 字符串类型 - username - 用户账号的用户名 - 字符串类型 - 最多30个字符 - 规定用户的用户名唯一,即不能出现不同账号具有相同的用户名,用户账号的用户名默认为用户账号 - userRegisterTime - 用户账号注册的时间 - 日期时间类型 - 格式:yyyy-mm-dd HH:MM:SS - userLoginTime - 用户账号最近一次的登录时间 - 日期时间类型 - 格式:yyyy-mm-dd HH:MM:SS - userRealName - 用户的真实姓名 - 字符串类型 - 最多30个字符 - userIdCardNumber - 用户的身份证号码 - 字符串类型 - userGender - 用户性别 - 字符串类型 - 取值:'F' - 女性;'M' - 男性 - 默认为男性 - userEmail - 用户邮箱 - 字符串类型 - 最多30个字符 - smsVerificationCode - 短信验证码 - 字符串类型 ### 💝 常量 - USER_ACCOUNT_LENGTH - 用户账号的长度(位数) - 整数类型 - 14位 - 根据用户账号的格式 mdyyyymmddxxxx,为14位 - USER_TELEPHONE_LENGTH - 用户手机号位数(长度) - 整数类型 - 11位 - USER_TELEPHONE_REGULAR - 用户手机号格式的正则表达式 - 正则 - `/^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/` - USER_PASSWORD_MAX_LENGTH - 用户账号密码的最大长度(位数) - 整数类型 - 16位 - USER_PASSWORD_MIN_LENGTH - 用户账号密码的最小长度(位数) - 整数类型 - 8位 - USER_PASSWORD_SECRET - 用于在用户进行登录时,在用户账号密码加上后缀,加强安全性,和用户的密码之间以点进行分隔 - 字符串类型 - 取值:123456 - USER_HEAD_PICTURE - 用户默认头像的路径地址 - 字符串类型 - 取值:`require('@/assets/user-default-head-picture.jpg')` - 如果用户没有上传头像则默认使用该路径对应的图片作为用户头像 - USERNAME_MAX_LENGTH - 用户账号用户名最大长度 - 整数类型 - 30 - USER_REAL_NAME_MAX_LENGTH - 用户姓名的最大长度 - 整数类型 - 30 - USER_ID_CARD_NUMBER_LENGTH - 用户身份证号码的位数(长度) - 整数类型 - 18位 - USER_GENDER_MALE - 用户性别,男性 - 字符串类型 - 取值:'M' - USER_GENDER_FEMALE - 用户性别,女性 - 字符串类型 - 取值:'F' - USER_EMAIL_MAX_LENGTH - 用户邮箱的最大长度 - 整数类型 - 30 - SMS_VERIFICATION_CODE_LENGTH - 短信验证码位数(长度) - 整数类型 - 6位 - SMS_VERIFICATION_CODE_SEND_AGAIN_WAIT_TIME - 短信验证码再次发送需要等待的时间,单位秒 - 整数类型 - 60秒 - SMS_VERIFICATION_CODE_VALID_TIME - 短信验证码的有效时间,单位秒 - 整数类型 - 300秒 - SMS_VERIFICATION_FIXED - 没有开启短信验证码功能时,使用的固定默认短信验证码 - 字符串类型 - 取值:'111111' - TOKEN_VALID_TIME - token的有效时间 - 整数类型 - 5天,单位天 ## 💖 与请求和响应相关 ### 💝 常量 - MARK_ERROR - 请求处理失败的标记 - 取值为0 - MARK_SUCCESS - 请求处理成功的标记 - 取值为1 - MARK_INVALID - 过期的标记 - 取值为2 ### 💝 响应数据对象的键 - mark - 本次请求处理成功或失败的标记 - 取值:0 - 失败;1 - 成功 - message - 本次请求响应的信息描述 - 字符串类型 - data - 请求响应回来的数据 - 对象/字典类型 ## 💖 用于美多商城服务端功能控制的常量 - IS_OPEN_SMS_VERIFICATION - 是否开启短信验证码的验证功能 - 布尔类型 - 如果不开启短信验证码的验证功能,则在用户注册过程中,使用短信验证码111111,即可实现注册;如果开启短信验证码的验证功能,则用户在注册过程中将会收到短信验证码,用户需要根据收到的短信验证码进行注册。 --- # 💘 美多商城数据库表的说明 - user_account_info - 用户账号信息表 - 包含字段: - id:用户账号编号,int - user_account:用户账号,char(14) - user_telephone:用户手机号,char(11) - user_password:用户账号密码,varchar(16) - username:用户账号的用户名,varchar() - user_register_time:用户注册时间,datetime - user_login_time:用户最近一次登录时间,datetime - user_head_picture:用户账号头像,varchar(255) - user_personal_info_id:外键,关联user_personal_info表的id字段,int - user_personal_info - 用户个人信息表 - 包含字段: - id:用户个人信息编号,int - user_real_name:用户真实姓名,varchar(30) - user_id_card_number:用户身份证号码,char(30) - user_gender:用户性别,char(1),取值:‘F’/‘M’ - user_telephone:用户手机号,char(11) - user_email:用户邮箱,varchar(30) - user_receive_address_info - 用户收货地址信息表 - 包含字段: - id > 其余表的字段的相关说明在表的创建语句中 --- # 💘 美多商城数据库SQL语句 ## 💖 数据库的创建 ```sql CREATE DATABASE meiduo; ``` ## 💖 表的创建 ```sql -- 表的创建 # 创建用户个人信息表 CREATE TABLE user_personal_info ( id INT PRIMARY KEY AUTO_INCREMENT, user_real_name VARCHAR(30) DEFAULT NULL COMMENT '用户真实姓名', user_id_card_number CHAR(18) UNIQUE DEFAULT NULL COMMENT '用户身份证号码', user_gender CHAR(1) DEFAULT 'M' CHECK ( user_gender IN ('F', 'M') ) COMMENT '用户性别', user_telephone CHAR(11) UNIQUE NOT NULL COMMENT '用户手机号', user_email VARCHAR(30) DEFAULT NULL COMMENT '用户邮箱' ); # 创建用户账号信息表 CREATE TABLE user_account_info ( id INT PRIMARY KEY AUTO_INCREMENT, user_account CHAR(14) UNIQUE NOT NULL COMMENT '用户账号', user_telephone CHAR(11) UNIQUE NOT NULL COMMENT '用户手机号', user_password VARCHAR(16) NOT NULL COMMENT '用户账号密码', username VARCHAR(30) UNIQUE DEFAULT NULL COMMENT '用户账号的用户名,默认为用户账号', user_register_time DATETIME DEFAULT NOW() NOT NULL COMMENT '用户账号的注册时间', user_login_time DATETIME DEFAULT NOW() NOT NULL COMMENT '用户账号最近一次登录时间', user_personal_info_id INT NOT NULL COMMENT '外键,关联用户个人信息表的id,级联删除', CONSTRAINT fk_user_account_info_user_personal_info_id FOREIGN KEY (user_personal_info_id) REFERENCES user_personal_info (id) ON DELETE CASCADE ); # 创建用户收货地址信息表 CREATE TABLE user_receive_address_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户收货地址信息编号', receive_name VARCHAR(30) NOT NULL COMMENT '收货人姓名', receive_telephone CHAR(11) NOT NULL COMMENT '收货人手机号', receive_address VARCHAR(255) NOT NULL COMMENT '收货地址', is_default CHAR(1) DEFAULT 'F' NOT NULL COMMENT '是否为默认地址', user_account_info_id INT NOT NULL COMMENT '外键,关联用户账号信息表的id,级联删除', CONSTRAINT fk_user_receive_address_info_user_account_info_id FOREIGN KEY (user_account_info_id) REFERENCES user_account_info (id) ON DELETE CASCADE ); # 商品信息表 CREATE TABLE goods_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品信息编号', goods_name VARCHAR(255) NOT NULL COMMENT '商品名', goods_price DECIMAL(10, 2) NOT NULL COMMENT '商品价格', goods_category INT NOT NULL COMMENT '商品类别', goods_sale_number INT NOT NULL DEFAULT 0 COMMENT '商品销量', goods_comment_number INT NOT NULL DEFAULT 0 COMMENT '商品评价数', goods_freight DECIMAL(6, 2) NOT NULL DEFAULT 0 COMMENT '商品运费' ); # 商品参数信息表 CREATE TABLE goods_param_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品参数信息编号', param VARCHAR(255) NOT NULL COMMENT '商品参数', param_category INT DEFAULT 0 NOT NULL COMMENT '商品参数类别', goods_info_id INT COMMENT '外键,关联商品信息表的id,商品信息被删除时级联删除', CONSTRAINT fk_goods_param_info_goods_info_id FOREIGN KEY (goods_info_id) REFERENCES goods_info (id) ON DELETE CASCADE ); # 商品图片信息表 CREATE TABLE goods_picture_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品图片信息编号', goods_picture_path VARCHAR(255) NOT NULL COMMENT '商品图片的存放路径', goods_info_id INT COMMENT '外键,关联商品信息表的id,商品信息被删除时级联删除', CONSTRAINT fk_goods_picture_info_goods_info_id FOREIGN KEY (goods_info_id) REFERENCES goods_info (id) ON DELETE CASCADE ); # 订单信息表 CREATE TABLE order_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '订单信息编号', order_status CHAR(1) NOT NULL DEFAULT 'F' COMMENT '订单状态', goods_number INT NOT NULL COMMENT '购买商品数量', pay_time DATETIME NOT NULL COMMENT '订单支付时间', pay_money DECIMAL(10, 2) NOT NULL COMMENT '订单支付金额', create_time DATETIME NOT NULL COMMENT '订单创建时间', user_account_info_id INT COMMENT '外键,关联用户账号信息表id,用户被删除时设置为空', goods_info_id INT COMMENT '外键,关联商品信息表的id,商品信息被删除时设置为空', user_receive_address_id INT COMMENT '外键,关联用户收货地址信息表的id,用户收货地址信息被删除时设置为空', CONSTRAINT fk_order_info_user_account_info_id FOREIGN KEY (user_account_info_id) REFERENCES user_account_info (id) ON DELETE SET NULL, CONSTRAINT fk_order_info_goods_info_id FOREIGN KEY (goods_info_id) REFERENCES goods_info (id) ON DELETE SET NULL, CONSTRAINT fk_order_info_user_receive_address_id FOREIGN KEY (user_receive_address_id) REFERENCES user_receive_address_info (id) ON DELETE SET NULL ); # 用户商品评价信息表 CREATE TABLE user_goods_comment_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户商品评价信息编号', comment_score INT NOT NULL DEFAULT 0 COMMENT '商品评价评分', comment_description VARCHAR(255) DEFAULT '' COMMENT '商品评价', is_anonymous CHAR(1) NOT NULL DEFAULT 'F' COMMENT '是否为匿名评价', user_account_info_id INT COMMENT '外键,关联用户账号信息表的id,用户信息被删除时设置为空', goods_info_id INT COMMENT '外键,关联商品信息表的id,商品信息被删除时设置为空', CONSTRAINT fk_user_goods_comment_info_user_account_info_id FOREIGN KEY (user_account_info_id) REFERENCES user_account_info (id) ON DELETE SET NULL, CONSTRAINT fk_user_goods_comment_info_goods_info_id FOREIGN KEY (goods_info_id) REFERENCES goods_info (id) ON DELETE SET NULL ); ``` ## 💖 表的修改 ```sql -- 表的修改 # 向用户账号信息表中新增用户头像路径地址列 ALTER TABLE user_account_info ADD COLUMN user_head_picture VARCHAR(255) DEFAULT '' NOT NULL COMMENT '用户头像路径'; ``` ## 💖 触发器的创建 ```sql -- 触发器的创建 # 向用户账号信息表插入数据时,先向用户个人信息表插入该用户账号对应的用户个人信息 CREATE TRIGGER before_insert_user_account_info BEFORE INSERT ON user_account_info FOR EACH ROW BEGIN # 向用户个人信息表中新增数据记录,向该记录中填写用户手机号 INSERT INTO user_personal_info(user_telephone) VALUES (NEW.user_telephone); # 将新增的用户个人信息对应的id填写到新增的用户账号信息记录中 SET NEW.user_personal_info_id = ( SELECT id FROM user_personal_info WHERE user_telephone = NEW.user_telephone ); # 默认用户账号的用户名为用户账号 SET NEW.username = NEW.user_account; END; ``` ## 💖 视图的创建 ```sql -- 视图的创建 # 创建用于用户登录的视图 CREATE OR REPLACE VIEW v_user_account_info_login AS SELECT user_account, user_telephone, user_password FROM user_account_info; ``` --- # 💘 美多商城后台前端项目的创建与配置 美多商城的后台前端项目的创建与配置和美多商城前台前端项目的创建和配置类似,这里不再讲述。 # 💘 美多商城后台后端 美多商城的后台后端服务器与美多商城前台后端服务器使用同一个服务器。这里也不在讲述 --- # 手机号重复注册 --- # 💘💖💝💗💓💞💕💟💔❤️‍🔥🧡💛💚💙