电商免费网站入口,关于网站备案的公告,企业邮箱号是什么样的格式,外贸网站模板目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功#xff1a; 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具#xff1a;Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap… 目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与ApiOperation 四、基于JWT和ThreadLocal动态获取员工ID1. 在pom.xml中引入JWT依赖2. 在application.yaml中配置JWT参数3. 使用JWT获取当前登录员工ID4. 通过拦截器解析JWT5. 使用ThreadLocal传递员工ID6. 在Service中获取员工ID 五、DTO的使用原因1. 实体类 Employee2. DTO EmployeeDTO3. 使用 DTO 的场景3.1 查询员工信息3.2 更新员工信息3.3 新增员工 4. DTO、VO和实体类的区别 六、为什么使用 XML 注解而不是 MyBatis 注解1. 使用 XML 注解的原因1.1 动态 SQL 支持1.2 SQL 与代码分离1.3 复用性1.4 工具支持 2. MyBatis 注解的局限性2.1 动态 SQL 支持有限2.2 可读性差2.3 维护困难 七、Spring Boot 的请求映射规则1. 类级别路径2. 方法级别路径2.1 分页查询2.2 根据 ID 查询菜品2.3 修改菜品2.4 新增菜品2.5 批量删除菜品 3. 如何区分不同的功能4. 示例请求4.1 新增菜品4.2 修改菜品4.3 批量删除菜品4.4 分页查询菜品4.5 根据 ID 查询菜品 八、接口设计中的是否必须原则1. 请求参数说明1.1 Java代码分析1.1.1 必需参数 1.2 XML映射文件分析1.2.1 可选参数 2. 返回响应说明2.1 Java代码分析2.1.1 必需参数2.1.2 可选参数 2.2 返回响应示例2.2.1 成功响应带数据2.2.2 成功响应不带数据2.2.3 失败响应 九、阿里云OSS配置指南1. 注册阿里云OSS账号2. 获取关键信息3. 更新配置文件4, 重新运行服务 视频链接黑马程序员Java项目实战《苍穹外卖》最适合新手的SpringBootSSM的企业级Java项目实战 网盘资料苍穹外卖讲义前后端源码 一、在本地部署并启动Nginx服务
在开发过程中我们经常需要使用Nginx来部署前端项目或作为反向代理服务器。
1. 解压Nginx压缩包
首先确保你已经从黑马程序员资料中下载了Nginx的压缩包。接下来按照以下步骤解压
选择解压路径
将Nginx压缩包解压到一个全英文路径中。例如D:\nginx注意路径中不要包含中文或特殊字符否则可能会导致Nginx无法正常运行。
2. 启动Nginx服务
解压完成后按照以下步骤启动Nginx
进入Nginx目录
打开解压后的Nginx文件夹找到nginx.exe文件。路径通常为C:\nginx\nginx.exe启动Nginx
双击nginx.exe文件启动Nginx服务。启动后Nginx会在后台运行你可以在任务管理器中看到nginx.exe进程。
3. 验证Nginx是否启动成功
打开浏览器访问以下地址其中80是默认端口可省略不写http://localhost:80如果看到此页面说明Nginx已成功启动。 注意Nginx默认不会随系统自动启动因此每次重启电脑后都需要手动启动Nginx 二、导入接口文档
在开发过程中接口管理平台是团队协作和项目管理的重要工具。以下是几个常用平台的对比
1. 黑马程序员提供的YApi平台
地址http://yapi.smart-xwork.cn/状态已弃用功能适合用于接口管理和文档生成。
2. YApi Pro平台
地址https://yapi.pro/问题需要挂梯子才能访问且极易卡顿使用体验不佳。
3. 推荐工具Apifox
地址https://apifox.com/优势 无需梯子即可访问。性能流畅支持接口文档、Mock数据、自动化测试等功能。支持导入YApi数据格式的接口文档方便无缝迁移现有项目。 三、Swagger
Swagger 是一种用于设计、构建、记录和使用 RESTful Web 服务的开源框架。它提供了一套工具帮助开发者设计、构建、文档化和测试 API。
启动服务访问 http://localhost:8080/doc.html
1. 常用注解
通过注解可以控制生成的接口文档使接口文档拥有更好的可读性常用注解如下
注解说明Api用在类上例如Controller表示对类的说明ApiModel用在类上例如entity、DTO、VOApiModelProperty用在属性上描述属性信息ApiOperation用在方法上例如Controller的方法说明方法的用途、作用
1.1 Api与ApiModel 1.2 ApiModelProperty与ApiOperation 四、基于JWT和ThreadLocal动态获取员工ID
在开发员工管理系统时新增员工时需要记录创建人和修改人的ID。如果直接使用固定值会导致数据不准确无法真实反映操作者。
public void save(EmployeeDTO employeeDTO) {Employee employee new Employee();employee.setCreateUser(10L); // 固定值employee.setUpdateUser(10L); // 固定值employeeMapper.insert(employee);
}因此我们要通过JWT和ThreadLocal动态获取当前登录员工的ID并实现数据的准确记录。在使用 JWTJSON Web Token进行身份验证时通常需要在项目中引入相关的依赖库并在配置文件中设置 JWT 的参数。以下是如何在 Spring Boot 项目中引入 JWT 并进行配置的简单说明
1. 在pom.xml中引入JWT依赖
为了使用 JWT我们需要引入一个 JWT 库比如 java-jwt由 Auth0 提供。
dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion4.4.0/version !-- 使用最新版本 --
/dependency2. 在application.yaml中配置JWT参数
在 application.yaml 文件中定义 JWT 的相关配置例如密钥、过期时间和令牌名称。
sky:jwt:# 设置 JWT 签名加密时使用的秘钥admin-secret-key: itcast# 设置 JWT 过期时间单位毫秒admin-ttl: 7200000 # 2小时# 设置前端传递过来的令牌名称admin-token-name: tokenadmin-secret-key用于签名和验证 JWT 的密钥。必须保密且长度足够复杂以确保安全性。admin-ttlJWT 的有效期以毫秒为单位。例如7200000 表示 2 小时。admin-token-name前端传递 JWT 时使用的参数名称。例如前端可能会在请求头或请求参数中传递 tokenxxx。
3. 使用JWT获取当前登录员工ID
员工登录成功后系统会生成JWT令牌并返回给前端。JWT中包含了当前登录员工的ID信息。
/*** 员工管理*/
RestController
RequestMapping(/admin/employee)
Api(tags 员工相关接口)
Slf4j
public class EmployeeController {Autowiredprivate EmployeeService employeeService;Autowiredprivate JwtProperties jwtProperties;/*** 登录** param employeeLoginDTO* return*/PostMapping(/login)public ResultEmployeeLoginVO login(RequestBody EmployeeLoginDTO employeeLoginDTO) {log.info(员工登录{}, employeeLoginDTO);Employee employee employeeService.login(employeeLoginDTO);//登录成功后生成jwt令牌MapString, 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);}...
}4. 通过拦截器解析JWT
在每次请求时前端会携带JWT令牌。通过拦截器解析JWT获取当前登录员工的ID。
/*** jwt令牌校验的拦截器*/
Component
Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** param request* param response* param handler* return* throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法直接放行return true;}//1、从请求头中获取令牌String token request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌try {log.info(jwt校验:{}, token);Claims claims JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);Long empId Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());log.info(当前员工id, empId);BaseContext.setCurrentId(empId);//3、通过放行return true;} catch (Exception ex) {//4、不通过响应401状态码response.setStatus(401);return false;}}
}
5. 使用ThreadLocal传递员工ID
通过ThreadLocal实现线程隔离将当前登录员工的ID传递给Service层。
public class BaseContext {public static ThreadLocalLong threadLocal new ThreadLocal();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}
}6. 在Service中获取员工ID
在Service层中从ThreadLocal中获取当前登录员工的ID并设置为创建人和修改人。
public void save(EmployeeDTO employeeDTO) {Employee employee new Employee();employee.setCreateUser(BaseContext.getCurrentId()); // 动态获取当前登录员工IDemployee.setUpdateUser(BaseContext.getCurrentId()); // 动态获取当前登录员工IDemployeeMapper.insert(employee);
}五、DTO的使用原因
在项目中Employee 是实体类Entity用于表示数据库中的员工记录而 EmployeeDTO 是数据传输对象DTO用于在不同层之间传递数据。以下是使用 DTO 的主要原因和优势
1. 实体类 Employee
package com.sky.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.time.LocalDateTime;Data
Builder
NoArgsConstructor
AllArgsConstructor
public class Employee implements Serializable {private static final long serialVersionUID 1L;private Long id; // 员工IDprivate String username; // 用户名private String name; // 姓名private String password; // 密码敏感字段private String phone; // 手机号private String sex; // 性别private String idNumber; // 身份证号private Integer status; // 状态private LocalDateTime createTime; // 创建时间内部字段private LocalDateTime updateTime; // 更新时间内部字段private Long createUser; // 创建人内部字段private Long updateUser; // 更新人内部字段
}2. DTO EmployeeDTO
package com.sky.dto;import lombok.Data;
import java.io.Serializable;Data
public class EmployeeDTO implements Serializable {private Long id; // 员工IDprivate String username; // 用户名private String name; // 姓名private String phone; // 手机号private String sex; // 性别private String idNumber; // 身份证号
}3. 使用 DTO 的场景
3.1 查询员工信息
前端只需要员工的基本信息如 id、username、name、phone、sex、idNumber。后端返回 EmployeeDTO过滤掉敏感字段如 password和内部字段如 createTime。
3.2 更新员工信息
前端传递 EmployeeDTO 作为请求体后端根据 DTO 更新员工信息。避免前端传递不必要的字段如 password、createTime。
3.3 新增员工
前端传递 EmployeeDTO 作为请求体后端将 DTO 转换为实体类并保存到数据库。避免前端传递内部字段如 createTime、updateTime。
4. DTO、VO和实体类的区别
特性DTOVOEntity目的数据传输数据展示或封装值表示数据库中的数据结构使用场景跨层数据传输如Controller-Service展示层或领域模型数据库操作、业务逻辑可变性可变通常有setter通常不可变无setter可变用于持久化和业务逻辑字段与传输需求相关与展示或业务逻辑相关与数据库表字段严格对应行为通常无行为可能包含简单行为如格式化包含业务逻辑和验证规则示例UserDTOUserVOUserEntity 六、为什么使用 XML 注解而不是 MyBatis 注解
1. 使用 XML 注解的原因
1.1 动态 SQL 支持
XML 提供了强大的动态 SQL 支持例如 if、foreach、choose 等标签。在复杂的查询场景中动态 SQL 可以更灵活地构建 SQL 语句。
1.2 SQL 与代码分离
将 SQL 语句写在 XML 文件中可以使 SQL 与 Java 代码分离便于维护和管理。对于复杂的 SQL 语句XML 文件的可读性更高。
1.3 复用性
XML 文件中的 SQL 语句可以在多个 Mapper 接口中复用。例如可以在不同的 Mapper 接口中引用同一个 SQL 片段。
1.4 工具支持
MyBatis 提供了丰富的工具支持 XML 文件的编写和调试。例如MyBatis Generator 可以自动生成 XML 映射文件。
2. MyBatis 注解的局限性
2.1 动态 SQL 支持有限
MyBatis 注解对动态 SQL 的支持较弱复杂的 SQL 语句难以用注解实现。例如Select 注解无法直接实现 foreach 这样的动态 SQL。
select idgetSetmealIdsByDishIds resultTypejava.lang.Longselect setmeal_id from setmeal_dish where dish_id inforeach collectiondishIds itemdishId separator, open( close)#{dishId}/foreach
/select2.2 可读性差
复杂的 SQL 语句写在注解中会导致代码冗长可读性差。例如一个包含多个条件的查询语句会显得非常混乱。
2.3 维护困难
SQL 语句与 Java 代码混合在一起维护起来不如 XML 文件方便。修改 SQL 语句时需要重新编译 Java 代码。
你提到的代码中有两个 PutMapping 注解没有指定路径这意味着它们默认映射到类级别的路径 /admin/dish。以下是对这个问题的详细解释 七、Spring Boot 的请求映射规则
在 Spring Boot 中请求的映射是通过 类级别的 RequestMapping 和 方法级别的 PutMapping、GetMapping 等注解 共同决定的。
类级别的 RequestMapping 定义了该类中所有方法的公共路径前缀。例如RequestMapping(/admin/dish) 表示该类中的所有方法都映射到 /admin/dish 路径下。管理端发出的请求统一使用/admin作为前缀。用户端发出的请求统一使用/user作为前缀。 方法级别的 PutMapping、GetMapping 等 定义了具体的 HTTP 方法和路径。如果方法级别的注解没有指定路径则默认使用类级别的路径。
1. 类级别路径
RestController
RequestMapping(/admin/dish)
public class DishController {// 方法定义...
}所有方法的公共路径前缀是 /admin/dish。
2. 方法级别路径
2.1 分页查询
GetMapping(/page)
public ResultPageResult page(DishPageQueryDTO dishPageQueryDTO) {// 方法实现...
}完整路径是 /admin/dish/page。
2.2 根据 ID 查询菜品
GetMapping(/{id})
public ResultDishVO getById(PathVariable Long id) {// 方法实现...
}完整路径是 /admin/dish/{id}。
2.3 修改菜品
PutMapping
public Result update(RequestBody DishDTO dishDTO) {// 方法实现...
}由于 PutMapping 没有指定路径默认使用类级别的路径 /admin/dish。
2.4 新增菜品
PostMapping
public Result save(RequestBody DishDTO dishDTO) {// 方法实现...
}由于 PostMapping 没有指定路径默认使用类级别的路径 /admin/dish。
2.5 批量删除菜品
DeleteMapping
public Result delete(RequestParam ListLong ids) {// 方法实现...
}由于 DeleteMapping 没有指定路径默认使用类级别的路径 /admin/dish。
3. 如何区分不同的功能
Spring Boot 通过 HTTP 方法 来区分不同的功能。例如
HTTP 方法路径功能POST/admin/dish新增菜品PUT/admin/dish修改菜品DELETE/admin/dish批量删除菜品GET/admin/dish/page分页查询菜品GET/admin/dish/{id}根据 ID 查询菜品
4. 示例请求
4.1 新增菜品
HTTP 方法POSTURL/admin/dish请求体{name: 宫保鸡丁,price: 38.0,flavors: [{name: 微辣,value: 少辣}]
}4.2 修改菜品
HTTP 方法PUTURL/admin/dish请求体{id: 1,name: 宫保鸡丁,price: 40.0,flavors: [{name: 微辣,value: 少辣}]
}4.3 批量删除菜品
HTTP 方法DELETEURL/admin/dish?ids1,2,3请求参数ids1,2,3
4.4 分页查询菜品
HTTP 方法GETURL/admin/dish/page?page1pageSize10请求参数page1pageSize10
4.5 根据 ID 查询菜品
HTTP 方法GETURL/admin/dish/1路径参数id1 八、接口设计中的是否必须原则
参数的必需与非必需性是通过不同的方式来体现的以下是具体案例
1. 请求参数说明 从接口文档中可以看到请求参数包括以下几项
参数名类型说明必需性示例值categoryIdstring分类id可选101namestring菜品名称可选官保鸡丁pagestring页码必需1pageSizestring每页记录数必需10statusstring分类状态可选1 必需参数 page 和 pageSize 是分页查询的必需参数用于指定查询的页码和每页的记录数。 可选参数 categoryId、name 和 status 是可选参数用于过滤查询结果。
1.1 Java代码分析
在此 Java 代码中DishPageQueryDTO 是一个数据传输对象DTO用于封装分页查询的参数。以下是代码的详细分析
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {// 1. 使用 PageHelper 进行分页PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());// 2. 调用 Mapper 进行查询PageDishVO page dishMapper.pageQuery(dishPageQueryDTO);// 3. 返回分页结果return new PageResult(page.getTotal(), page.getResult());
}1.1.1 必需参数
dishPageQueryDTO.getPage() 和 dishPageQueryDTO.getPageSize() 是分页查询的必需参数。如果这两个参数为空或未提供分页功能将无法正常工作。
1.2 XML映射文件分析
在 SQL 代码中动态 SQL 语句根据传入的参数生成查询条件。以下是代码的详细分析
select idpageQuery resultTypecom.sky.vo.DishVOselect d.* , c.name as categoryName from dish d left outer join category c on d.category_id c.idwhereif testname ! nulland d.name like concat(%,#{name},%)/ifif testcategoryId ! nulland d.category_id #{categoryId}/ifif teststatus ! nulland d.status #{status}/if/whereorder by d.create_time desc
/select1.2.1 可选参数
name、categoryId 和 status 是可选参数通过 if 标签动态生成查询条件。如果某个参数为 null则对应的条件不会添加到 SQL 查询中。
通过这种设计分页查询接口既满足了基本的查询需求又提供了灵活的过滤选项适用于不同的业务场景。 2. 返回响应说明
在 API 设计中返回响应的数据结构通常需要遵循一定的规范以确保客户端能够准确处理和理解服务器的响应。ResultT 类是一个典型的统一响应格式根据不同的设计需求我们发现 code 一定是必需的而 msg 和 data 在某些情况下是可选的而在某些情况可能是必需的。接下来我们将详细探讨这种设计背后的原因。 2.1 Java代码分析
package com.sky.result;import lombok.Data;import java.io.Serializable;/*** 后端统一返回结果* param T*/
Data
public class ResultT implements Serializable {private Integer code; //编码1成功0和其它数字为失败private String msg; //错误信息private T data; //数据public static T ResultT success() {ResultT result new ResultT();result.code 1;return result;}public static T ResultT success(T object) {ResultT result new ResultT();result.data object;result.code 1;return result;}public static T ResultT error(String msg) {Result result new Result();result.msg msg;result.code 0;return result;}}2.1.1 必需参数
code 是必需的
作用code 用于表示请求的处理结果状态通常是一个数字。 例如 1 表示成功0 或其他数字表示失败。 为什么必需 明确状态客户端需要知道请求是否成功。code 提供了一个明确的状态标识客户端可以根据它决定后续操作。标准化统一的 code 值可以让客户端以一致的方式处理所有 API 的响应。错误处理当请求失败时code 可以帮助客户端快速定位问题类型如权限不足、资源不存在等。
2.1.2 可选参数
msg 是可选的
作用msg 用于提供额外的错误信息或成功提示。 例如成功时msg 可以为空或包含提示信息如“操作成功”失败时msg 可以包含具体的错误描述如“用户未找到”。 为什么可选 成功时可能不需要在请求成功的情况下客户端可能只需要 data而不需要额外的提示信息。减少冗余如果 msg 是必需的即使没有实际意义的信息如成功时的默认提示也需要返回这会增加响应的冗余。灵活性在某些场景下错误信息可能由其他方式提供如日志或专门的错误处理机制因此 msg 可以省略。
data 是可选的
作用data 用于承载实际的响应数据。 例如查询接口返回的列表或对象创建接口返回的新创建的资源。 为什么可选 某些操作不需要返回数据例如删除操作或简单的状态更新操作可能不需要返回任何数据。减少冗余如果 data 是必需的即使没有数据也需要返回一个空对象或 null这会增加响应的冗余。灵活性某些接口可能只需要返回状态信息如 code 和 msg而不需要额外的数据。
2.2 返回响应示例
2.2.1 成功响应带数据
{code: 1,msg: 操作成功,data: {id: 123,name: John Doe}
}2.2.2 成功响应不带数据
{code: 1
}2.2.3 失败响应
{code: 0,msg: 用户未找到
}这种设计符合 API 设计的最佳实践能够满足大多数场景的需求同时保持简洁和一致性。 九、阿里云OSS配置指南
在使用对象存储服务时可能会遇到Bucket失效的情况从而导致服务无法正常运行。为了解决这个问题我们需要重新配置一个新的Bucket并更新相关配置文件。参考教程Java利用阿里云OSS/本地存储实现文件上传功能
1. 注册阿里云OSS账号
首先访问阿里云-对象存储OSS官网注册账号。新用户可免费试用20GB存储空间有效期为3个月。 2. 获取关键信息
注册完成后获取以下四个关键信息
EndpointOSS服务的访问地址。Access Key ID用于身份验证的访问密钥ID。Access Key Secret用于身份验证的访问密钥。Bucket Name新创建的Bucket名称。 3. 更新配置文件
将上述获取的信息填写到对应的YAML配置文件sky-server/src/main/resources/application-dev.yml目录下
sky:datasource:driver-class-name: com.mysql.cj.jdbc.Driverhost: localhostport: 3306database: sky_take_outusername: your_usernamepassword: your_passwordalioss:endpoint: your_endpointaccess-key-id: your_accessKeyIdaccess-key-secret: your_keySecretbucket-name: your_bucketName# endpoint: oss-cn-hangzhou.aliyuncs.com
# access-key-id: LTAI5tPeFLzsPPT8gG3LPW64
# access-key-secret: U6k1brOZ8gaOIXv3nXbulGTUzy6Pd7
# bucket-name: sky-take-out4, 重新运行服务
保存配置文件后重新运行服务并打开前端页面发现图片能够正常上传了 并且我们可以在自己的对象存储OSS文件管理中监控到从前端页面上传的图片文件