苍穹外卖项目实战

软件开发整体介绍

软件开发流程

需求分析

  • 需求规格说明书:形成文档介绍
  • 产品原型:通过静态网页展示业务功能

设计

  • UI设计:将页面各个方面的细节设计完善
  • 数据库设计:先设计E-R图,然后再具体设计表的字段和类型等详细细节
  • 接口设计:就是设计接口文档(使用Apifox)

编码

  • 项目代码:业务逻辑的代码
  • 单元测试:用于测试项目代码的单元测试(开发人员自测)

测试

  • 测试用例:以接口为单位编写测试用例
  • 测试报告:对测试情况进行报告

上线运维

  • 软件环境安装:安装运行环境
  • 配置:进行一些线上的配置,如Nginx

角色分工

  • 项目经理:对整个项目负责,任务分配、把控进度
  • 产品经理:进行需求调研,输出需求调研文档、产品原型等
  • UI设计师:根据产品原型输出界面效果图
  • 架构师:项目整体架构设计、技术选型等
  • 开发工程师:代码实现
  • 测试工程师:编写测试用例,输出测试报告
  • 运维工程师:软件环境搭建、项目上线

软件环境

  • 开发环境(development):开发人员在开发阶段使用的环境,一般外部用户无法访问
  • 测试环境(testing):专门给测试人员使用的环境,用于测试项目,一般外部用户无法访问
  • 生产环境(production):即线上环境,正式提供对外服务的环境

Apifox等相关应用也应该采用与此统一的三种软件环境

苍穹外卖项目介绍

项目介绍

image-20250125095402962

image-20250125095622802

产品原型

image-20250125100337243

技术选型

image-20250125102924973

开发环境搭建

前端环境搭建

image-20250125105841391

后端环境搭建

注意,此处的搭建方式和目录结构并不是最佳实践,实际目录结构要根据具体项目来进行具体的架构设计

image-20250125105918916

image-20250125110227200

image-20250125110343659

image-20250125110700061

搭建好之后提交git仓库

数据库设计文档

image-20250125112438572

注意,这里的数据库设计并非最佳实践,实际上数据库的表名的最佳实践应该为模块名_功能点,如此更容易后期拓展其他模块

另外,用户名(账号)和密码这里做的是不错的,都应该是varchar,有的项目中账号使用数字,是不正确的做法

序号 数据表名 中文名称
1 employee 员工表
2 category 分类表
3 dish 菜品表
4 dish_flavor 菜品口味表
5 setmeal 套餐表
6 setmeal_dish 套餐菜品关系表
7 user 用户表
8 address_book 地址表
9 shopping_cart 购物车表
10 orders 订单表
11 order_detail 订单明细表

1. employee

employee表为员工表,用于存储商家内部的员工信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 姓名
username varchar(32) 用户名 唯一
password varchar(64) 密码
phone varchar(11) 手机号
sex varchar(2) 性别
id_number varchar(18) 身份证号
status int 账号状态 1正常 0锁定
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

2. category

category表为分类表,用于存储商品的分类信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 分类名称 唯一
type int 分类类型 1菜品分类 2套餐分类
sort int 排序字段 用于分类数据的排序
status int 状态 1启用 0禁用
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

3. dish

dish表为菜品表,用于存储菜品的信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 菜品名称 唯一
category_id bigint 分类id 逻辑外键
price decimal(10,2) 菜品价格
image varchar(255) 图片路径
description varchar(255) 菜品描述
status int 售卖状态 1起售 0停售
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

4. dish_flavor

dish_flavor表为菜品口味表,用于存储菜品的口味信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
dish_id bigint 菜品id 逻辑外键
name varchar(32) 口味名称
value varchar(255) 口味值

5. setmeal

setmeal表为套餐表,用于存储套餐的信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 套餐名称 唯一
category_id bigint 分类id 逻辑外键
price decimal(10,2) 套餐价格
image varchar(255) 图片路径
description varchar(255) 套餐描述
status int 售卖状态 1起售 0停售
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

6. setmeal_dish

setmeal_dish表为套餐菜品关系表,用于存储套餐和菜品的关联关系。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
setmeal_id bigint 套餐id 逻辑外键
dish_id bigint 菜品id 逻辑外键
name varchar(32) 菜品名称 冗余字段
price decimal(10,2) 菜品单价 冗余字段
copies int 菜品份数

7. user

user表为用户表,用于存储C端用户的信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
openid varchar(45) 微信用户的唯一标识
name varchar(32) 用户姓名
phone varchar(11) 手机号
sex varchar(2) 性别
id_number varchar(18) 身份证号
avatar varchar(500) 微信用户头像路径
create_time datetime 注册时间

8. address_book

address_book表为地址表,用于存储C端用户的收货地址信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
user_id bigint 用户id 逻辑外键
consignee varchar(50) 收货人
sex varchar(2) 性别
phone varchar(11) 手机号
province_code varchar(12) 省份编码
province_name varchar(32) 省份名称
city_code varchar(12) 城市编码
city_name varchar(32) 城市名称
district_code varchar(12) 区县编码
district_name varchar(32) 区县名称
detail varchar(200) 详细地址信息 具体到门牌号
label varchar(100) 标签 公司、家、学校
is_default tinyint(1) 是否默认地址 1是 0否

9. shopping_cart

shopping_cart表为购物车表,用于存储C端用户的购物车信息。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 商品名称
image varchar(255) 商品图片路径
user_id bigint 用户id 逻辑外键
dish_id bigint 菜品id 逻辑外键
setmeal_id bigint 套餐id 逻辑外键
dish_flavor varchar(50) 菜品口味
number int 商品数量
amount decimal(10,2) 商品单价
create_time datetime 创建时间

10. orders

orders表为订单表,用于存储C端用户的订单数据。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
number varchar(50) 订单号
status int 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消
user_id bigint 用户id 逻辑外键
address_book_id bigint 地址id 逻辑外键
order_time datetime 下单时间
checkout_time datetime 付款时间
pay_method int 支付方式 1微信支付 2支付宝支付
pay_status tinyint 支付状态 0未支付 1已支付 2退款
amount decimal(10,2) 订单金额
remark varchar(100) 备注信息
phone varchar(11) 手机号
address varchar(255) 详细地址信息
user_name varchar(32) 用户姓名
consignee varchar(32) 收货人
cancel_reason varchar(255) 订单取消原因
rejection_reason varchar(255) 拒单原因
cancel_time datetime 订单取消时间
estimated_delivery_time datetime 预计送达时间
delivery_status tinyint 配送状态 1立即送出 0选择具体时间
delivery_time datetime 送达时间
pack_amount int 打包费
tableware_number int 餐具数量
tableware_status tinyint 餐具数量状态 1按餐量提供 0选择具体数量

11. order_detail

order_detail表为订单明细表,用于存储C端用户的订单明细数据。具体表结构如下:

字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 商品名称
image varchar(255) 商品图片路径
order_id bigint 订单id 逻辑外键
dish_id bigint 菜品id 逻辑外键
setmeal_id bigint 套餐id 逻辑外键
dish_flavor varchar(50) 菜品口味
number int 商品数量
amount decimal(10,2) 商品单价

前后端联调

前后端都搭建好之后,通过实现登录等基础功能,进行前后端的联调,尽早联调防止后续出现问题

image-20250125113042698

Nginx反向代理

正向代理:代理客户端(隐藏用户)

反向代理:代理服务端(隐藏服务器)

image-20250125120554075

image-20250125120845201

image-20250125121040531

image-20250125121244660

image-20250125121444149

image-20250125121533164

补充知识

image-20250126103429705

最好是前端和后端都进行加密,如此一来更加安全

image-20250126120756918

实际开发过程中,最好是结合使用HTTP状态码、业务状态码以及业务信息,HTTP状态码负责划分错误大体信息,业务状态码负责明确错误详细信息,业务信息用来提供用户友好型的提示信息

另外,业务状态码可以采用阿里文档中的状态码,其中没有的状态码可以自己新增,需要遵守文档状态码的分类模式

因此Result枚举中应该如下所示:

image-20250126124937528

全局自定义异常捕获应该如下所示:

image-20250126125800124

前后端分离开发流程

image-20250126153641670

使用Swagger进行接口测试

注意,实际开发过程中使用Apifox进行调试和测试即可,比这个方便很多很多

但是实际开发过程中,Swagger还是需要使用的,因为其放在源码中,可以便捷的生成接口文档,算是一种冗余策略(Swagger注解和注释都要有)

image-20250126155038528

image-20250126155048167

image-20250126155308059

image-20250126155400037

image-20250126161550579

业务开发

JWT令牌完整流程

image-20250128101638833

JWT令牌原理

JWT令牌分为三部分:头部,载荷,签名

头部和载荷是不加密的,而签名是通过不可逆的签名算法获取到的密文

签名=不可逆的签名算法(头部+载荷,密钥)

JWT令牌=头部.载荷.不可逆的签名算法(头部+载荷,密钥)

前端与后端交互时,将JWT令牌给到后端,后端通过令牌中公开的头部和载荷,以及只有后端自己知道的密钥,再次进行签名,将此签名和前端传过来的前面对比一下,一样的话就通过校验,不一样就不通过

注:不可逆的签名算法指的是计算过程只能从左到右,不能从右到左的算法,例如36×78=2808,这个算式从左到右很好算,但是你只知道2808,想要知道算式左侧是啥就很难办)

如何获取到JWT令牌中存储的信息

image-20250128112148972

每个请求的处理都是独立的线程,而ThreadLocal(线程局部存储)提供了一种独立存储每个线程局部变量的方式,其提供的方法可以以线程独立的方式存取当前线程的数据,因此我们可以在解析JWT的时候,可以将JWT信息放到ThreadLocal中,从而使当前线程随时能访问JWT中的信息

public class JwtContext {
    // 使用 ThreadLocal 来存储每个线程的 JWT 信息
    private static ThreadLocal<String> jwtToken = new ThreadLocal<>();

    // 设置当前线程的 JWT 信息
    public static void setJwtToken(String token) {
        jwtToken.set(token);
    }

    // 获取当前线程的 JWT 信息
    public static String getJwtToken() {
        return jwtToken.get();
    }

    // 清理线程的 JWT 信息
    public static void clear() {
        jwtToken.remove();
    }
}

1. ThreadLocal 的工作机制

ThreadLocal 为每个线程提供一个独立的存储空间。每个线程在访问 ThreadLocal 时,都会看到与自己相关的数据,而不会与其他线程共享数据。

  • 当你通过 ThreadLocal.set() 方法存储数据时,数据会被存储在当前线程的局部存储空间中。
  • 通过 ThreadLocal.get() 获取数据时,只能访问当前线程的存储数据。

2. 线程生命周期

  • 当线程结束时,ThreadLocal 中存储的数据会被销毁。这意味着如果某个请求处理结束,线程被回收,那么该线程在 ThreadLocal 中存储的任何数据都会随之销毁。
  • 如果 线程池复用线程(例如在 Web 应用中使用的线程池),则线程并不会完全销毁,而是会在下一次使用时继续工作。在这种情况下,ThreadLocal 数据可能在下一次线程复用时仍然存在,除非你显式地调用 ThreadLocal.remove() 来清除它。

3. 避免内存泄漏

在某些环境中,尤其是线程池复用的情况下,线程的生命周期比请求的生命周期长。如果在请求结束后没有显式地清除 ThreadLocal 中的内容,它可能会导致内存泄漏,因为 ThreadLocal 会保持对数据的引用,而这些线程可能长时间存在于线程池中。

为了避免这种情况,应该显式地清除 ThreadLocal 中的数据

4. 总结

  • 当线程结束时,ThreadLocal 存储的数据会被销毁,不会占用额外的内存
  • 但如果使用 线程池,在复用线程时,ThreadLocal 中的内容不会自动清除,这时需要手动调用 ThreadLocal.remove() 来确保清理,避免内存泄漏。

因此,为了确保每个请求处理完成后 ThreadLocal 中的数据被清理,应该在请求处理的结束阶段调用 ThreadLocal.remove(),尤其是在使用线程池的情况下。

除特殊说明,博客文章均为东篱原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇