《苍穹外卖》SpringBoot后端开发项目重点知识整理(DAY1 to DAY3)
目录
- 一、在本地部署并启动Nginx服务
- 1. 解压Nginx压缩包
- 2. 启动Nginx服务
- 3. 验证Nginx是否启动成功:
- 二、导入接口文档
- 1. 黑马程序员提供的YApi平台
- 2. YApi Pro平台
- 3. 推荐工具:Apifox
- 三、Swagger
- 1. 常用注解
- 1.1 @Api与@ApiModel
- 1.2 @ApiModelProperty与@ApiOperation
- 四、基于JWT和ThreadLocal动态获取员工ID
- 1. 在pom.xml中引入JWT依赖
- 2. 在application.yaml中配置JWT参数
- 3. 使用JWT获取当前登录员工ID
- 4. 通过拦截器解析JWT
- 5. 使用ThreadLocal传递员工ID
- 6. 在Service中获取员工ID
- 五、DTO的使用原因
- 1. 实体类 Employee
- 2. DTO EmployeeDTO
- 3. 使用 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项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级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、VO |
@ApiModelProperty | 用在属性上,描述属性信息 |
@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,并实现数据的准确记录。在使用 JWT(JSON Web Token)进行身份验证时,通常需要在项目中引入相关的依赖库,并在配置文件中设置 JWT 的参数。以下是如何在 Spring Boot 项目中引入 JWT 并进行配置的简单说明:
1. 在pom.xml中引入JWT依赖
为了使用 JWT,我们需要引入一个 JWT 库,比如 java-jwt
(由 Auth0 提供)。
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version> <!-- 使用最新版本 -->
</dependency>
2. 在application.yaml中配置JWT参数
在 application.yaml
文件中定义 JWT 的相关配置,例如密钥、过期时间和令牌名称。
sky:jwt:# 设置 JWT 签名加密时使用的秘钥admin-secret-key: itcast# 设置 JWT 过期时间(单位:毫秒)admin-ttl: 7200000 # 2小时# 设置前端传递过来的令牌名称admin-token-name: token
admin-secret-key
:用于签名和验证 JWT 的密钥。必须保密,且长度足够复杂以确保安全性。admin-ttl
:JWT 的有效期(以毫秒为单位)。例如,7200000
表示 2 小时。admin-token-name
:前端传递 JWT 时使用的参数名称。例如,前端可能会在请求头或请求参数中传递token=xxx
。
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 Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {log.info("员工登录:{}", employeeLoginDTO);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);}...
}
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 ThreadLocal<Long> 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和实体类的区别
特性 | DTO | VO | Entity |
---|---|---|---|
目的 | 数据传输 | 数据展示或封装值 | 表示数据库中的数据结构 |
使用场景 | 跨层数据传输(如Controller-Service) | 展示层或领域模型 | 数据库操作、业务逻辑 |
可变性 | 可变(通常有setter) | 通常不可变(无setter) | 可变(用于持久化和业务逻辑) |
字段 | 与传输需求相关 | 与展示或业务逻辑相关 | 与数据库表字段严格对应 |
行为 | 通常无行为 | 可能包含简单行为(如格式化) | 包含业务逻辑和验证规则 |
示例 | UserDTO | UserVO | UserEntity |
六、为什么使用 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 id="getSetmealIdsByDishIds" resultType="java.lang.Long">select setmeal_id from setmeal_dish where dish_id in<foreach collection="dishIds" item="dishId" separator="," open="(" close=")">#{dishId}</foreach>
</select>
2.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 Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {// 方法实现...
}
- 完整路径是
/admin/dish/page
。
2.2 根据 ID 查询菜品
@GetMapping("/{id}")
public Result<DishVO> 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 List<Long> 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 方法:
POST
- URL:
/admin/dish
- 请求体:
{"name": "宫保鸡丁","price": 38.0,"flavors": [{"name": "微辣","value": "少辣"}] }
4.2 修改菜品
- HTTP 方法:
PUT
- URL:
/admin/dish
- 请求体:
{"id": 1,"name": "宫保鸡丁","price": 40.0,"flavors": [{"name": "微辣","value": "少辣"}] }
4.3 批量删除菜品
- HTTP 方法:
DELETE
- URL:
/admin/dish?ids=1,2,3
- 请求参数:
ids=1,2,3
4.4 分页查询菜品
- HTTP 方法:
GET
- URL:
/admin/dish/page?page=1&pageSize=10
- 请求参数:
page=1&pageSize=10
4.5 根据 ID 查询菜品
- HTTP 方法:
GET
- URL:
/admin/dish/1
- 路径参数:
id=1
八、接口设计中的是否必须原则
参数的必需与非必需性是通过不同的方式来体现的,以下是具体案例
1. 请求参数说明
从接口文档中可以看到,请求参数包括以下几项:
参数名 | 类型 | 说明 | 必需性 | 示例值 |
---|---|---|---|---|
categoryId | string | 分类id | 可选 | 101 |
name | string | 菜品名称 | 可选 | 官保鸡丁 |
page | string | 页码 | 必需 | 1 |
pageSize | string | 每页记录数 | 必需 | 10 |
status | string | 分类状态 | 可选 | 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 进行查询Page<DishVO> 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 id="pageQuery" resultType="com.sky.vo.DishVO">select d.* , c.name as categoryName from dish d left outer join category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%',#{name},'%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desc
</select>
1.2.1 可选参数
name
、categoryId
和status
是可选参数,通过<if>
标签动态生成查询条件。- 如果某个参数为
null
,则对应的条件不会添加到 SQL 查询中。
通过这种设计,分页查询接口既满足了基本的查询需求,又提供了灵活的过滤选项,适用于不同的业务场景。
2. 返回响应说明
在 API 设计中,返回响应的数据结构通常需要遵循一定的规范,以确保客户端能够准确处理和理解服务器的响应。Result<T>
类是一个典型的统一响应格式,根据不同的设计需求,我们发现 code
一定是必需的,而 msg
和 data
在某些情况下是可选的,而在某些情况可能是必需的。接下来,我们将详细探讨这种设计背后的原因。
2.1 Java代码分析
package com.sky.result;import lombok.Data;import java.io.Serializable;`/*** 后端统一返回结果* @param <T>*/
@Data
public class Result<T> implements Serializable {private Integer code; //编码:1成功,0和其它数字为失败private String msg; //错误信息private T data; //数据public static <T> Result<T> success() {Result<T> result = new Result<T>();result.code = 1;return result;}public static <T> Result<T> success(T object) {Result<T> result = new Result<T>();result.data = object;result.code = 1;return result;}public static <T> Result<T> 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. 获取关键信息
注册完成后,获取以下四个关键信息:
Endpoint
:OSS服务的访问地址。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-out
4, 重新运行服务
保存配置文件后,重新运行服务并打开前端页面,发现图片能够正常上传了!
并且我们可以在自己的对象存储OSS文件管理中监控到从前端页面上传的图片文件
相关文章:
《苍穹外卖》SpringBoot后端开发项目重点知识整理(DAY1 to DAY3)
目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功: 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具:Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…...
Mysql内置函数
日期函数: 例如: select current_date();---打印日期 select current_time()----打印时间 select current_timestamp();打印时间戳 select now(); ----日期时间 select date(‘2020-10-01 00:00:00’); ----提取日期进行打印: 也可以配合…...
一文了解汽车图像传感器
2024年底,安森美做了题为"How Automotive Image Sensors Transform the Future of Autonomous Driving"的演讲,这里结合其内容对自动驾驶图像传感器做一个介绍。 当前的自动驾驶感知技术主要有两大技术路线:一种是仅使用摄像头作为传感器进行信息采集的纯…...
cocos creator使用mesh修改图片为圆形,减少使用mask,j减少drawcall,优化性能
cocos creator版本2.4.11 一个mask占用drawcall 3个以上,针对游戏中技能图标,cd,以及多玩家头像,是有很大优化空间 1.上代码,只适合单独图片的,不适合在图集中的图片 const { ccclass, property } cc._decorator;c…...
面向高质量视频生成的扩散模型方法-算法、架构与实现【附核心代码】
目录 算法原理 架构 代码示例 算法原理 正向扩散过程:从真实的视频数据开始,逐步向其中添加噪声,随着时间步 t 的增加,噪声添加得越来越多,最终将原始视频数据变成纯噪声。数学上,t 时刻的视频数据与 t…...
vue3框架的响应式依赖追踪机制
当存在一个响应式变量于视图中发生改变时会更新当前组件的所以视图显示,但是没有视图中不写这个响应式变量就就算修改该变量也不会修改视图,这是为什么?我们能否可以理解宽泛的理解为vue组件的更新就是视图的更新,单当视图中不存在…...
Ubuntu本地部署Open manus(完全免费可用)
目录 1.介绍 2.环境搭建 3.更改配置 4.运行 1.介绍 关于由于邀请码的限制,导致很多用户无法顺利体验manus带来的ai agent体验,但是最近开源的open manus是另一个不错的选择。 先来看看运行的结果: 我让open manus帮我打开小米的主页&am…...
MacOS Big Sur 11 新机安装brew wget python3.12 exo
MacOS Big Sur 11,算是很老的系统了,所以装起来brew有点费劲。 首先安装brew 官网: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 官网加速: 按照官网的方法࿰…...
基于USB Key的Web系统双因素认证解决方案:构建安全与便捷的登录体系
摘要 在网络安全威胁日益严峻的背景下,传统的“用户名密码”认证方式已难以应对钓鱼攻击、密码窃取等风险。上海安当基于USB Key技术,推出了一套面向Web系统的双因素认证解决方案,通过硬件与密码学的深度融合,实现用户身份的高强度…...
项目工坊 | Python驱动淘宝信息爬虫
目录 前言 1 完整代码 2 代码解读 2.1 导入模块 2.2 定义 TaoBao 类 2.3 search_infor_price_from_web 方法 2.3.1 获取下载路径 2.3.2 设置浏览器选项 2.3.3 反爬虫处理 2.3.4 启动浏览器 2.3.5 修改浏览器属性 2.3.6 设置下载行为 2.3.7 打开淘宝登录页面 2.3.…...
数据安全_笔记系列12:数据访问控制中的三个管理员权限划分
数据安全_笔记系列04:数据访问控制中的“三权分立”机制详解 文章链接:https://blog.csdn.net/fen_fen/article/details/145852242 数据安全_笔记系列12:数据访问控制中的三个管理员权限划分 这三个角色的职责和权限划分。 系统应具有相互独…...
通义千问本地配置并实现微调
通义千问本地配置并实现微调 最小Qwen模型大小942mb from modelscope import snapshot_download model_dir = snapshot_download(“qwen/Qwen2.5-0.5B”, cache_dir=“./models2.5”) Qwen2.5-0.5B:942MB from modelscope import snapshot_download model_dir = snapshot_d…...
文件上传漏洞(upload靶场)
目录 Pass-01:前端绕过 方法一:浏览器禁用js 方法二:直接修改或删除js脚本 方法三:修改后缀绕过 Pass-02:服务器检测 Pess-03:黑名单绕过 Pass-04:.htaccess文件 Pass-05:windows特性和user.ini 方法一:php.自动解析为ph…...
Leetcode 刷题记录 05 —— 普通数组
本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答。 目录 01 最大子数组和 方法一:动态规划(卡达尼算法) 方法…...
随机过程的核心概念与Matlab实现
摘要 本文系统讲解随机过程的核心理论与Matlab实现,涵盖随机变量分布、蒙特卡罗仿真、信息熵计算及平稳过程特性。通过高斯、瑞利分布的生成代码、蒙特卡罗积分估计、窄带信号仿真等案例,结合功率谱分析与自相关函数推导,演示随机过程建模与…...
git worktree的使用
git worktree 是 Git 提供的一个强大功能,允许你在同一个仓库中同时创建多个工作目录,每个目录对应一个分支,从而实现并行开发。以下是 git worktree 的常用命令和使用方法: 1. 创建新的工作目录(Worktree)…...
delphi 正则提取html中的内容
function ExtractTextFromHTML(const HTML: string): string; var RegEx: TRegEx; begin Result := HTML; // 移除<script>标签及其内容 Result := TRegEx.Replace(Result, <script.*?>.*?</script>, , [roIgnoreCase, roSingleLine]); // 移除<s…...
深度学习系列79:Text2sql调研
参考 https://github.com/topics/text-to-sql 这里是一些资源:https://github.com/eosphoros-ai/Awesome-Text2SQL/blob/main/README.zh.md 这里是综述文章:https://zhuanlan.zhihu.com/p/647249972 1. 数据集 Spider: 一个跨域的复杂text2sql数据集&a…...
分布式锁—5.Redisson的读写锁一
大纲 1.Redisson读写锁RedissonReadWriteLock概述 2.读锁RedissonReadLock的获取读锁逻辑 3.写锁RedissonWriteLock的获取写锁逻辑 4.读锁RedissonReadLock的读读不互斥逻辑 5.RedissonReadLock和RedissonWriteLock的读写互斥逻辑 6.写锁RedissonWriteLock的写写互斥逻辑…...
【AI热点】Manus技术细致洞察报告(篇2)
针对大家对Manus产品褒贬不一的现象,基于近期对Manus的多方实测、公开信息与开源竞品的比对分析而撰写,旨在为从业者、技术爱好者以及潜在用户提供一个较为系统、专业的视角。报告将围绕Manus的核心原理、功能特点、技术亮点、常见应用场景与不足&#x…...
虚幻基础:动画层接口
文章目录 动画层:动画图表中的函数接口:名字,没有实现。动画层接口:由动画蓝图实现1.动画层可直接调用实现功能2.动画层接口必须安装3.动画层默认使用本身实现4.动画层也可使用其他动画蓝图实现,但必须在角色蓝图中关联…...
C#的简写技巧
在C#中,有许多简写技巧可以让你的代码更加简洁高效,以下是一些常见的: 1. 变量声明与初始化 使用 var 关键字 :当你能从初始化表达式中推断出变量类型时,可以使用 var 关键字代替显式的类型声明。例如: va…...
【js逆向】
地址:aHR0cHM6Ly93d3cud2VpYm90b3AuY24vMi4wLw f12进入 debugger,过debugger 查看预览数据 全局搜索 请求网址中的 api.weibotop.cn 在下方疑似找到了加密和解密的函数 断点调试 控制台输出 那个n就是 常见的 cryptoJs库 const cryptoJs require(cry…...
汽车一键启动按钮更换注意事项
汽车一键启动开关更换教程 一键启动开关是现代汽车中常见的便捷配置,但随着时间的推移,这个部件可能会出现失灵的情况。当一键启动开关发生故障时,许多车主选择自行更换。以下是整理的一键启动开关更换教程: 更换前的准备 选择匹…...
为wordpress自定义一个留言表单并可以在后台进行管理的实现方法
要为WordPress添加留言表单功能并实现后台管理,你可以按照以下步骤操作: 1. 创建留言表单 首先,你需要创建一个留言表单。可以使用插件(如Contact Form 7)或手动编写代码。 使用Contact Form 7插件 安装并激活Contact Form 7插件。 创建…...
Hive函数、外部表和分区表
目录 1. Hive自定义函数1.1 pom.xml中依赖配置1.2 Hive自定义函数示例代码1.3 打包并演示 2. 外部表3. 分区表参考 1. Hive自定义函数 Hive中简单的自定义函数需要继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF,并且实现其中的三个方法: 方法功…...
再聊 Flutter Riverpod ,注解模式下的 Riverpod 有什么特别之处,还有发展方向
三年前我们通过 《Flutter Riverpod 全面深入解析》 深入理解了 riverpod 的内部实现,而时隔三年之后,如今Riverpod 的主流模式已经是注解,那今天就让我们来聊聊 riverpod 的注解有什么特殊之处。 前言 在此之前,我们需要先回忆…...
每日一题----------String 和StringBuffer和StringBuiler重点
本质:是一个char字符数组存储字符串 总结: 1.如果字符串存在大量的修改操作,一般使用StringBuffer或者StringBuilder。 2.如果字符串存在大量的修改操作,并且单线程的情况,使用StringBuilder。 3.如果字符串存在大…...
使用AI一步一步实现若依前端(5)
功能5:侧边栏菜单动态显示 功能4:首页使用Layout布局 功能3:点击登录按钮实现页面跳转 功能2:静态登录界面 功能1:创建前端项目前言 在若依中,侧边栏显示的菜单项,是根据登录用户的角色动态显…...
如何在需求分析阶段考虑未来扩展性
在需求分析阶段考虑未来扩展性的关键在于 前瞻规划、灵活架构、标准设计。其中,前瞻规划尤为重要,因为通过全面分析业务发展趋势与技术演进,能够在初期设计阶段预留足够扩展空间,降低后期改造成本,为企业长期发展奠定坚…...
平面机械臂运动学分析
平面机械臂运动学分析 一 整体概述1 研究步骤: 二 正向1 几何分析2 matlab 仿真模拟(1)实现效果(2)matlab代码: 3 DH矩阵计算法(1)计算公式(2)计算结果验证&a…...
1688店铺所有商品数据接口详解
一、接口概述淘宝开放平台提供 1688.items.onsale.get/taobao.item_search_shop 接口,可批量获取店铺在售商品列表,包含商品 ID、标题、价格、销量、图片等核心信息。该接口适用于商品库管理、竞品监控、数据分析等场景 二、接口调用流程 前期准…...
华为hcia——Datacom实验指南——三层交换和ARP的工作原理
什么是三层交换 三层交换是指连接在同一台三层交换机上,不同vlan用户,不同网段ip,通过vlanif接口进行数据交换。 什么是ARP协议 通过网络层的ip地址解析成数据链路层的mac地址。 说白了就是通过目标ip地址去问他对应的mac地址是多少。 A…...
手脑革命:拆解Manus AI如何用“执行智能体”重构生产力——中国团队突破硅谷未竟的技术深水区
第一章:Manus AI 的技术演进与行业背景 1.1 从工具到智能体:AI 技术的范式跃迁 人工智能的发展经历了从规则驱动(Rule-based)到统计学习(Statistical Learning),再到深度学习(Deep…...
【算法】二叉树的递归遍历
前序遍历 void preOrder(Node *node){if(node ! nullptr){cout << node->data_ << " ";preOrder(node->left_);preOrder(node->right_);}} 中序遍历 void inOrder(Node *node){if (node ! nullptr){inOrder(node->left_);cout << n…...
中级网络工程师面试题参考示例(1)
一、基础理论 1. OSI七层模型与TCP/IP四层模型的区别是什么?请举例说明第三层(网络层)和第四层(传输层)的核心协议。 参考答案: OSI七层模型分为物理层、数据链路层、网络层、传输层、会话层、表示层、应用…...
Java并发 vs 并行:本质区别与应用场景全解析(易混概念)
并发 vs 并行:本质区别与应用场景全解析(易混概念) 一、核心区别:从定义出发 在计算机科学中,并发(Concurrency) 和 并行(Parallelism) 是两种完全不同的任务处理模型&a…...
深度评测DeepSeek、ChatGPT O1和谷歌Gemini AI应用开发场景 - DeepSeek性能完胜!
下面我会展示我为期一周的实验结果,创作不宜,希望大家关注我,以后多多互3!前一阵我在互联网上看到很多关于DeepSeek R1的讨论,这个开源模型据说可以媲美,甚至优于像OpenAI o1这样的付费模型。 由于我在日常…...
【GoTeams】-5:引入Docker
本文目录 1. Dokcer-compose回顾下Docker知识编写docker-compose.yaml运行docker 2. 部署go服务编写dockerfile 1. Dokcer-compose 这里简单先用一下win版本的Docker,后期开发好了部署的时候再移植到服务器下进行docker部署。 输入命令docker-compose version 就可…...
mysql的Innodb最大支持的索引长度是多少,以及索引长度怎么计算
今天正好有空,来讲个之前粉丝经常问的一个知识,就是mysql的Innodb最大支持的索引长度是多少?以及索引长度怎么计算? 一、mysql的innodb引擎,创建索引最大支持的长度是多少字节? 不墨迹,直接说…...
深入解析 configService.addListener 使用中的注意事项
在使用 Nacos 的 configService.addListener 方法进行配置监听时,为了确保程序的稳定性、可靠性以及高效性,有诸多注意事项需要我们关注。下面将对这些关键要点进行详细阐述。 一、连接稳定性 1.1 网络连接问题 Nacos 客户端与服务端通过网络进行通信&…...
数据结构和算法--仅仅用于理解里面的术语,入门级别
数据结构和算法 预先知识:java 黑马前29节 cmd命令: 文件夹路径不区分大小写 E: dir:查看所有文件 cd 目录 :进入 cd… 返回上一级 cd 目录1\目录2 cd\ 回到根目录 cls 清屏 exit 退出 打开文件夹必须用cd 查找,但是文件不用&am…...
this.$nextTick() 作用及实现原理
1、原理和作用 2、更新任务推送到微任务队列后,vue是如何知道所有的更新任务执行完成了? vue将更新任务推送给微任务队列;当更新任务执行的时候,将回调队列任务推给微任务队列;通过微任务队列的原子性和先进先出机制&…...
C#常用的循环语句
在C#中,循环是一种控制结构,用于重复执行一组语句直到满足特定条件。C#提供了几种循环结构,包括for循环、while循环、do-while循环和foreach循环。每种循环都有其特定的用途和场景。下面我将逐一介绍这些循环的用法。 一、C#循环类型 1. fo…...
Android View 设置背景方式全解析
一、整体概述 在 Android 开发中,视图(View)的背景设置是构建用户界面的重要组成部分。一个合适的背景可以提升界面的美观度,增强用户体验。从简单的纯色背景到复杂的动态效果,背景设置不仅影响界面美观,还…...
HTTP拾技杂谈
HTTP拾技杂谈 简单聊聊HTTP中的那些东西 文章目录 HTTP拾技杂谈前言HTTP协议1.请求从客户端到服务器端的4个步骤一般客户端请求如下:服务端响应如下 2.Keep-AliveHTTP方法Cookie 总结 前言 超文本传输协议(Hypertext Transfer Protocol ,HT…...
网络安全之RSA算法
1978年就出现了这种算法,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字(RonRivest,AdiShamir和LeonardAdleman)命名。但RSA的安全性一直未能得到理论上的证…...
神经网络为什么要用 ReLU 增加非线性?
在神经网络中使用 ReLU(Rectified Linear Unit) 作为激活函数的主要目的是引入非线性,这是神经网络能够学习复杂模式和解决非线性问题的关键。 1. 为什么需要非线性? 1.1 线性模型的局限性 如果神经网络只使用线性激活函数&…...
ES10(2019)、ES11(2020) 新增特性(八)
目录 ES10(2019) Array.flat() Array.flatMap() String.trimStart()和String.trimEnd() Symbol.prototype.description Object.fromEntries() ES11(2020) Nullish coalescing Operator【空值运算符】 可选链(…...
利用MQ自动取消未支付超时订单最佳实践
一、利用MQ自动取消未支付超时订单最佳实践 1、基于 RocketMQ 延迟消息 1.1:延迟消息 当消息写入到 Broker 后,不会立刻被消费者消费,需要等待指定的时长后才可被消费处理的消息,称为延时消息。 1.2:实现流程 &am…...