目录
[1.1 软件开发流程](#1.1 软件开发流程)
[1.2 角色分工](#1.2 角色分工)
[1.3 软件环境](#1.3 软件环境)
[2.1 项目简介](#2.1 项目简介)
[2.2 产品原型](#2.2 产品原型)
[2.3 技术选型](#2.3 技术选型)
[3.1 前端环境搭建](#3.1 前端环境搭建)
[3.2 后端环境搭建](#3.2 后端环境搭建)
[3.2.1 项目结构](#3.2.1 项目结构)
[3.2.2 Git 版本控制](#3.2.2 Git 版本控制)
[3.2.3 数据库环境搭建](#3.2.3 数据库环境搭建)
[3.2.4 前后端联调](#3.2.4 前后端联调)
[3.2.5 Nginx 反向代理与负载均衡](#3.2.5 Nginx 反向代理与负载均衡)
[3.2.6 完善登录功能(MD5加密)](#3.2.6 完善登录功能(MD5加密))
[五、Swagger 接口文档工具](#五、Swagger 接口文档工具)
[5.1 介绍](#5.1 介绍)
[5.2 使用步骤](#5.2 使用步骤)
[5.3 常用注解](#5.3 常用注解)
本文是《苍穹外卖》项目实战的第一天课程记录,适合刚接触企业级开发的初学者。我们将从软件开发流程讲起,带你了解一个真实项目是如何从需求到上线的,并完成开发环境的搭建。
一、软件开发整体介绍
作为一名软件开发工程师,你需要了解软件开发的完整流程、团队角色分工以及常见的软件环境。
1.1 软件开发流程

一个软件项目从无到有,通常经历以下 5 个阶段:
| 阶段 | 主要工作 | 产出物 |
|---|---|---|
| 需求分析 | 产品经理调研需求,编写需求规格说明书、产品原型 | 需求文档、产品原型(HTML页面) |
| 设计 | UI设计、数据库设计、接口设计 | 界面效果图、数据库表结构、接口文档 |
| 编码 | 开发工程师编写代码,进行单元测试 | 项目代码、单元测试报告 |
| 测试 | 测试工程师功能测试、性能测试 | 测试报告 |
| 上线运维 | 运维工程师部署环境,项目上线 | 线上可运行的系统 |
产品原型:用网页形式展示页面布局、数据展示和交互效果,是开发的重要参考。
1.2 角色分工
一个标准的项目团队通常包含以下角色:
| 角色 | 职责 |
|---|---|
| 项目经理 | 整体把控进度、任务分配 |
| 产品经理 | 需求调研、输出需求文档和原型 |
| UI设计师 | 设计界面效果图 |
| 架构师 | 技术选型、架构设计 |
| 开发工程师 | 功能代码实现(我们的角色) |
| 测试工程师 | 编写测试用例、输出测试报告 |
| 运维工程师 | 环境搭建、项目上线 |
实际项目中,人员可能身兼数职,比如项目经理兼任架构师。
1.3 软件环境

开发过程中会接触三种环境:
-
开发环境(development):开发人员本地使用,外部无法访问。
-
测试环境(testing):独立服务器,供测试人员使用。
-
生产环境(production):正式对外提供服务的线上环境。
项目开发完成后,依次经过开发 → 测试 → 生产环境的部署流程。
二、苍穹外卖项目介绍
2.1 项目简介

"苍穹外卖"是一款专为餐饮企业定制的软件产品,包含:

-
管理端:供餐厅内部员工使用,管理分类、菜品、套餐、订单、员工等。
-
用户端:供消费者使用的小程序,支持浏览菜品、加购物车、下单、支付等。
2.2 产品原型
产品原型用于展示业务功能,并非最终页面效果。课程资料中提供了管理端和用户端两份原型文件。
管理端主要功能:

-
登录/退出
-
员工管理
-
分类管理
-
菜品管理
-
套餐管理
-
订单管理
-
数据统计
用户端主要功能:

-
微信登录
-
点餐(分类/菜品浏览)
-
购物车
-
订单支付
-
个人信息、历史订单
2.3 技术选型
项目采用前后端分离架构,技术栈如下:

三、开发环境搭建

3.1 前端环境搭建
前端工程基于 Nginx 部署。从资料中找到 Nginx,解压到非中文目录 ,双击 nginx.exe 启动,访问 http://localhost:80 即可看到管理端页面。

3.2 后端环境搭建
3.2.1 项目结构
后端基于 Maven 分模块构建,父工程 sky-take-out 下包含:
| 模块 | 作用 |
|---|---|
sky-common |
公共类:工具类、常量、异常等 |
sky-pojo |
实体类:Entity、DTO、VO |
sky-server |
核心服务:Controller、Service、Mapper、配置等 |
sky-common 包结构:
-
constant:常量
-
context:上下文
-
enumeration:枚举
-
exception:自定义异常
-
json:JSON处理
-
properties:配置属性
-
result:统一返回结果
-
utils:工具类
sky-pojo 包结构:
-
Entity:与数据库表对应
-
DTO:数据传输对象
-
VO:视图对象(返回给前端)
-
POJO:普通Java对象
sky-server 包结构:
-
config:配置类
-
controller:控制器
-
interceptor:拦截器
-
mapper:数据访问层
-
service:业务层
-
SkyApplication:启动类
3.2.2 Git 版本控制
-
在 IDEA 中创建 Git 本地仓库。

-
在 Gitee 创建远程仓库。

-
提交代码到本地仓库(忽略
.idea、target等文件)。

-
添加远程仓库地址并推送。




3.2.3 数据库环境搭建
从资料中找到 sky.sql,直接在 MySQL 客户端中执行,会创建 11 张表:
| 表名 | 中文名 |
|---|---|
| employee | 员工表 |
| category | 分类表 |
| dish | 菜品表 |
| dish_flavor | 菜品口味表 |
| setmeal | 套餐表 |
| setmeal_dish | 套餐菜品关系表 |
| user | 用户表 |
| address_book | 地址表 |
| shopping_cart | 购物车表 |
| orders | 订单表 |
| order_detail | 订单明细表 |
3.2.4 前后端联调

后端初始工程已实现登录功能。启动后端服务,前端访问登录页面,输入用户名密码即可测试。
登录流程:
-
Controller 接收
EmployeeLoginDTO,调用 Service。在sky-server模块中,com.sky.controller.admin.EmployeeControllerjava/** * 登录 * * @param employeeLoginDTO * @return */ @PostMapping("/login") public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) { log.info("员工登录:{}", employeeLoginDTO); //调用service方法查询数据库 Employee employee = employeeService.login(employeeLoginDTO); //登录成功后,生成jwt令牌 Map<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.EMP_ID, employee.getId()); String token = JwtUtil.createJWT( jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims); EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() .id(employee.getId()) .userName(employee.getUsername()) .name(employee.getName()) .token(token) .build(); return Result.success(employeeLoginVO); } -
Service 根据用户名查询员工,校验密码、账号状态。在sky-server模块中,com.sky.service.impl.EmployeeServiceImpl
java/** * 员工登录 * * @param employeeLoginDTO * @return */ public Employee login(EmployeeLoginDTO employeeLoginDTO) { String username = employeeLoginDTO.getUsername(); String password = employeeLoginDTO.getPassword(); //1、根据用户名查询数据库中的数据 Employee employee = employeeMapper.getByUsername(username); //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定) if (employee == null) { //账号不存在 throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND); } //密码比对 if (!password.equals(employee.getPassword())) { //密码错误 throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR); } if (employee.getStatus() == StatusConstant.DISABLE) { //账号被锁定 throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED); } //3、返回实体对象 return employee; } -
登录成功生成 JWT 令牌,返回
EmployeeLoginVO。在sky-server模块中,com.sky.mapper.EmployeeMapperjava@Mapper public interface EmployeeMapper { /** * 根据用户名查询员工 * @param username * @return */ @Select("select * from employee where username = #{username}") Employee getByUsername(String username); }
Mapper 层 使用
@Select注解查询数据库。
3.2.5 Nginx 反向代理与负载均衡

问题 :前端请求 http://localhost/api/employee/login,后端接口 http://localhost:8080/admin/employee/login,地址不一致,如何请求到后端?

答案:Nginx 反向代理。
反向代理的好处:

-
提高访问速度(缓存)
-
负载均衡
-
保证后端服务安全(不暴露真实地址)
配置方式:
javascript
server{
listen 80;
server_name localhost;
location /api/{
proxy_pass http://localhost:8080/admin/; #反向代理
}
}
**proxy_pass:**该指令是用来设置代理服务器的地址,可以是主机名称,IP地址加端口号等形式。
如上代码的含义是:监听80端口号, 然后当我们访问 http://localhost:80/api/../..这样的接口的时候,它会通过 location /api/ {} 这样的反向代理到 http://localhost:8080/admin/上来
负载均衡配置:
javascript
upstream webservers{
server 192.168.100.128:8080;
server 192.168.100.129:8080;
}
server{
listen 80;
server_name localhost;
location /api/{
proxy_pass http://webservers/admin;#负载均衡
}
}
**upstream:**如果代理服务器是一组服务器的话,我们可以使用upstream指令配置后端服务器组。
如上代码的含义是:监听80端口号, 然后当我们访问 http://localhost:80/api/../..这样的接口的时候,它会通过 location /api/ {} 这样的反向代理到 http://webservers/admin,根据webservers名称找到一组服务器,根据设置的负载均衡策略(默认是轮询)转发到具体的服务器。
**注:**upstream后面的名称可自定义,但要上下保持一致
常见负载均衡策略:
| 名称 | 说明 |
|---|---|
| 轮询 | 默认方式 |
| weight | 权重方式,默认为1,权重越高,被分配的客户端请求就越多 |
| ip_hash | 依据ip分配方式,这样每个访客可以固定访问一个后端服务 |
| least_conn | 依据最少连接方式,把请求优先分配给连接数少的后端服务 |
| url_hash | 依据url分配方式,这样相同的url会被分配到同一个后端服务 |
| fair | 依据响应时间方式,响应时间短的服务将会被优先分配 |
3.2.6 完善登录功能(MD5加密)

问题:数据库中密码明文存储,不安全。

解决:使用 MD5 加密。
-
修改数据库中密码为 MD5 密文。
-
修改 Java 代码,将前端密码进行 MD5 加密后再比对:
java
/**
* 员工登录
*
* @param employeeLoginDTO
* @return
*/
public Employee login(EmployeeLoginDTO employeeLoginDTO) {
//1、根据用户名查询数据库中的数据
//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
//.......
//密码比对
// TODO 后期需要进行md5加密,然后再进行比对
password = DigestUtils.md5DigestAsHex(password.getBytes());
if (!password.equals(employee.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
//........
//3、返回实体对象
return employee;
}
四、导入接口文档

前后端分离开发模式下,接口定义至关重要。我们使用 Apifox中的YApi 平台管理接口文档。
操作步骤:
-
访问 YApi 平台,创建项目。

-
从资料中找到接口 JSON 文件(管理端、用户端)。

-
导入到 YApi 中。

五、Swagger 接口文档工具
5.1 介绍
Swagger (API Documentation & Design Tools for Teams | Swagger) 是一个用于生成、描述、调用 RESTful 接口的框架,能在线自动生成接口文档,方便前后端协作和测试。目前常用 knife4j 作为增强解决方案。
5.2 使用步骤
导入 Maven 坐标:
java
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
添加配置类(WebMvcConfiguration):
java
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
设置静态资源映射:
WebMvcConfiguration.java
java
/**
* 设置静态资源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
访问测试 :接口文档访问路径为 http://ip:port/doc.html ---> http://localhost:8080/doc.html

**思考:**通过 Swagger 就可以生成接口文档,那么我们就不需要 Yapi 了?
1、Yapi 是设计阶段使用的工具,管理和维护接口
2、Swagger 在开发阶段使用的框架,帮助后端开发人员做后端的接口测
5.3 常用注解
| 注解 | 说明 |
|---|---|
@Api |
用在类上,说明类的用途 |
@ApiModel |
用在类上,说明数据模型 |
@ApiModelProperty |
用在属性上,描述属性 |
@ApiOperation |
用在方法上,说明方法用途 |
示例:
在sky-pojo模块中
EmployeeLoginDTO.java
java
@Data
@ApiModel(description = "员工登录时传递的数据模型")
public class EmployeeLoginDTO implements Serializable {
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
}
在sky-server模块中
EmployeeController.java
java
/**
* 员工管理
*/
@RestController
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@Autowired
private JwtProperties jwtProperties;
/**
* 登录
*
* @param employeeLoginDTO
* @return
*/
@PostMapping("/login")
@ApiOperation(value = "员工登录")
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
//..............
}
/**
* 退出
*
* @return
*/
@PostMapping("/logout")
@ApiOperation("员工退出")
public Result<String> logout() {
return Result.success();
}
}
启动服务:访问http://localhost:8080/doc.html

六、总结
今天我们完成了:
-
了解软件开发全流程、角色分工、三种环境
-
认识苍穹外卖项目:管理端 + 用户端
-
搭建前端(Nginx)和后端(Maven多模块)环境
-
配置 Git、数据库,完成前后端联调
-
理解 Nginx 反向代理和负载均衡
-
使用 MD5 加密完善登录功能
-
导入接口文档,集成 Swagger(knife4j)生成在线接口文档