![](https://i-blog.csdnimg.cn/blog_migrate/d2bebc0990dd8211d8b68958a146e047.png)
Git版本控制
Linux从安装到实战&瑞吉外卖项目部署
Redis基础
![](https://i-blog.csdnimg.cn/blog_migrate/6e83443d6cf87cadfc41e596e6ba49c5.png)
Redis入门 redis.io
![](https://i-blog.csdnimg.cn/blog_migrate/99f3b0c0c034eead1a5925307952a4fe.png)
nosql没有表的概念
![](https://i-blog.csdnimg.cn/blog_migrate/d9d3efdb986a9ce8d7dbd15cabe9a522.png)
![](https://i-blog.csdnimg.cn/blog_migrate/720915be3130190ecf94238419beddf9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9fc3f4cc75be92b86ffc4894c86b1f3e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c547294c469cb97245fe88bf8a14317a.png)
注意关闭防火墙
systemctl stop firewalld
启动redis
src/redis-server ./redis.conf
![](https://i-blog.csdnimg.cn/blog_migrate/df1bdd98dbbde9315d52eaca131f2916.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d4ea28fb59c2fbed9ed6f10a79b03fd5.png)
数据类型
![](https://i-blog.csdnimg.cn/blog_migrate/916bb8171d6e99638e56eef10277cf03.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f0827c17e24b9bcd16b08e67f407799f.png)
常用命令
字符串 string 操作命令
![](https://i-blog.csdnimg.cn/blog_migrate/1065409726091b974b94e016936de09b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/50f43ea5063ebe565c4fc1a4a86deb35.png)
哈希 hash 操作命令
![](https://i-blog.csdnimg.cn/blog_migrate/39d823fdf503a1812f8a03b54f5185ba.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e8e428bf5de805eb1fbf040cb2b4048c.png)
列表list(类似 栈 )操作命令
![](https://i-blog.csdnimg.cn/blog_migrate/b60d465e740d81ac2e4228d289d15bf1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/03504756bff9644e9e302345d6193ac2.png)
集合set 操作命令
![](https://i-blog.csdnimg.cn/blog_migrate/61b53f51b34e1eeee03af426e9bb5fff.png)
![](https://i-blog.csdnimg.cn/blog_migrate/adfa1c90ae1036fbb71096fe0e61fcf1.png)
sdiff key1 [key2] :key1-key2;
有序集合 sorted set (zset) 操作命令
![](https://i-blog.csdnimg.cn/blog_migrate/4c214bd498d1385f896522f7727185b6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cc5fd4301292c06531801b3475527bf6.png)
通用命令
![](https://i-blog.csdnimg.cn/blog_migrate/cb032589f5e6bdb6444e9ba02478d682.png)
TTL return -1 表示永久;
在Java中操作Redis
介绍
![](https://i-blog.csdnimg.cn/blog_migrate/526277664af2205c58982e769f27d304.png)
Jedis
![](https://i-blog.csdnimg.cn/blog_migrate/40f5e95e5300bb0353c0175d59edc752.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6cd918e8c102d65c80fdd27b938a28b8.png)
Spring Data Redis
![](https://i-blog.csdnimg.cn/blog_migrate/f4b083a530c83bd367c910cc1a073beb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bace8f9954ab49443c633d1872a2e73c.png)
Redis服务默认会给16个数据库在redis.windows.conf里面修改
![](https://i-blog.csdnimg.cn/blog_migrate/77f06e03e897f015af2fd220421dc44f.png)
默认是在0号数据库操作,更换数据库:select 1
![](https://i-blog.csdnimg.cn/blog_migrate/ae0f9c3d14a10f0fe1bc235882a6f8d4.png)
String
![](https://i-blog.csdnimg.cn/blog_migrate/fbbe2d018b644c485110749de9e08995.png)
hash
![](https://i-blog.csdnimg.cn/blog_migrate/e2b141c36b27e66a28baf2903e4b3eff.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ac222fdd7159fb4b6b28afe83c86140f.png)
List
![](https://i-blog.csdnimg.cn/blog_migrate/f205dbe3b554865bb2e09a3446ab6c1c.png)
Set
![](https://i-blog.csdnimg.cn/blog_migrate/c7bfe73c8ec623b28bd31719dd045de2.png)
ZSet
![](https://i-blog.csdnimg.cn/blog_migrate/35f3843154fc07d9737df7924cf7a311.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bfcfbe8c9f0242d4ffb8b61312408730.png)
通用操作,针对不同数据类型都可以操作
![](https://i-blog.csdnimg.cn/blog_migrate/8fb7362e3b8390951c388c65daf03aa0.png)
项目优化-缓存优化
![](https://i-blog.csdnimg.cn/blog_migrate/ac4424e7f8ee839da6a295f9c23f361d.png)
环境搭建
![](https://i-blog.csdnimg.cn/blog_migrate/bd0c5966719589a252751d5d92059537.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a9418ab96a6aa7602421d8cf4aea732a.png)
配置RedisConfig:为了自定义序列化器
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//默认的Key序列化器为:JdkSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
缓存短信验证码
保存方式:Session-->Redis
![](https://i-blog.csdnimg.cn/blog_migrate/17a5d2c944ff3c27a54404dea128520f.png)
@Autowired
private RedisTemplate redisTemplate;
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session){
String phone = user.getPhone();//获取手机号
if(StringUtils.isNotEmpty(phone)){//手机号非空
//工具类 生成随机的4位验证码
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}",code);
//调用阿里云提供的短信服务API("签名","模板",手机号,动态验证码)完成发送短信
//SMSUtils.sendMessage("瑞吉外卖","",phone,code);
//需要将生成的验证码保存到Session
//session.setAttribute(phone,code);
//将生成的验证码保存到Session
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
return R.success("手机验证码短信发送成功");
}
return R.error("短信发送失败");
}
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session){
log.info(map.toString());
//获取手机号
String phone = map.get("phone").toString();
//获取验证码
String code = map.get("code").toString();
//从Session中获取保存的验证码
//Object codeInSession = session.getAttribute(phone);
//从redis中获得缓存的验证码
Object codeInSession = redisTemplate.opsForValue().get(phone);
//进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
if(codeInSession != null && codeInSession.equals(code)){
//如果能够比对成功,说明登录成功
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone,phone);
User user = userService.getOne(queryWrapper);
if(user == null){
//判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
user = new User();
user.setPhone(phone);
user.setStatus(1);
userService.save(user);
}
session.setAttribute("user",user.getId());
//如果用户登录成功,删除redis中缓存的验证码
redisTemplate.delete(phone);
return R.success(user);
}
return R.error("登录失败");
}
链接redis报错 ERROR org.springframework.boot. ; ERR invalid password;java.io.IOException:
缓存菜品数据
![](https://i-blog.csdnimg.cn/blog_migrate/1869bc3823467a3eba5f5a0de38add10.png)
@GetMapping("/list")//改造list
public R<List<DishDto>> list(Dish dish){
List<DishDto> dishDtoList =null;
//动态构造key
String key="dish_"+dish.getCategoryId()+"_"+dish.getStatus();//
//1.先从redis中获取缓存数据,按照菜单分类缓存
dishDtoList= (List<DishDto>) redisTemplate.opsForValue().get(key);
if(dishDtoList != null) {
//2.如果存在!=null,直接返回,不用查询数据库
return R.success(dishDtoList);
}
//构造查询条件
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(dish.getCategoryId() != null ,Dish::getCategoryId,dish.getCategoryId());
//添加条件,查询状态为1(起售状态)的菜品
queryWrapper.eq(Dish::getStatus,1);
//添加排序条件 排序顺序,创建时间倒序
queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
List<Dish> list = dishService.list(queryWrapper);
dishDtoList = list.stream().map((item) -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item,dishDto);
Long categoryId = item.getCategoryId();//分类id
//根据id查询分类对象
Category category = categoryService.getById(categoryId);
if(category != null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
}
//当前菜品的id
Long dishId = item.getId();
LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
//SQL:select * from dish_flavor where dish_id = ?
List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);
dishDto.setFlavors(dishFlavorList);
return dishDto;
}).collect(Collectors.toList());
//3.不存在,需要查询数据库,将查询到的数据缓存到redis
redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.HOURS);//60分钟
return R.success(dishDtoList);
}
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
log.info(dishDto.toString());
dishService.saveWithFlavor(dishDto);
//1.更新完 就清理所有缓存数据
//Set keys = redisTemplate.keys("dish_*");//获得所有以‘dish_’开头的key
//redisTemplate.delete(keys);
//2.精确 清理缓存数据(只清理 被更新类别 的缓存数据)
String key="dish_" + dishDto.getCategoryId()+"_1";
redisTemplate.delete(key);
return R.success("新增菜品成功");
}
@PutMapping//改造update&save
public R<String> update(@RequestBody DishDto dishDto){
log.info(dishDto.toString());
dishService.updateWithFlavor(dishDto);
//1.更新完 就清理所有缓存数据
//Set keys = redisTemplate.keys("dish_*");//获得所有以‘dish_’开头的key
//redisTemplate.delete(keys);
//2.精确 清理缓存数据(只清理 被更新类别 的缓存数据)
String key="dish_" + dishDto.getCategoryId()+"_1";
redisTemplate.delete(key);
return R.success("修改菜品成功");
}
Spring Cache框架
介绍 基于注解的缓存 (Cache译为缓存)
![](https://i-blog.csdnimg.cn/blog_migrate/d4425b8a9418dc5c3abda5ed588f6a8e.png)
常用注解
![](https://i-blog.csdnimg.cn/blog_migrate/c6f914e739cd305fcc97a6fac49250d3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e83b3fdf84bfaec07aa855b54d66578f.png)
#result: 代表方法返回值(condition关键字里没有result用unless代替); #root.method: 方法对象 ;
#root.args[0] / #p0 / #a0(a0我感觉也行) : 方法第一个参数
![](https://i-blog.csdnimg.cn/blog_migrate/5300a6e5f92a96eb4586ae84996478ea.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c46504415511c3a60ab1718584293395.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7408b8e8015cd7ff980349fc75149393.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2a5dad878a48c5cb541c9e789176b3c6.png)
使用方式
![](https://i-blog.csdnimg.cn/blog_migrate/df483a4211abd64f29a3b1014ded33f3.png)
缓存套餐数据
![](https://i-blog.csdnimg.cn/blog_migrate/339cb5411db476f149222c1107fda8aa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e7937fb8a06e6c16c3a0074a94607c50.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f6eeed9e314defb33a307d27dd2b8979.png)
项目优化2—读写分离
![](https://i-blog.csdnimg.cn/blog_migrate/9612499f3f4b22d214f3b88862c55077.png)
![](https://i-blog.csdnimg.cn/blog_migrate/071d9825113f8e5066c0390e2447a93b.png)
Mysql主从复制
![](https://i-blog.csdnimg.cn/blog_migrate/e60ba982113b70a4652fdfb96cfddba9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aa3cee6b52a104b80d1b1d49c3d40eb5.png)
配置主库Master
![](https://i-blog.csdnimg.cn/blog_migrate/2d96b909f7b42cdc095faa948259312c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/29748ce672a58e9c3dfaf7600edb50d1.png)
先登录 mysql -uroot -p1234(你的密码)
![](https://i-blog.csdnimg.cn/blog_migrate/c4da08ced724ef92598b9ca7aa08d0b0.png)
GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'Root@1234';(我的密码比他少两位)
![](https://i-blog.csdnimg.cn/blog_migrate/91cf71add1c8f7a7e7ad9b463722fb7a.png)
从库Slave #192.168.138.132; root@1234
![](https://i-blog.csdnimg.cn/blog_migrate/901d7c24ced6515fd8d13403a815e3ca.png)
关闭防火墙链接mysql
![](https://i-blog.csdnimg.cn/blog_migrate/06b322623b9ce76b8ccc5f82e6dc2d92.png)
![](https://i-blog.csdnimg.cn/blog_migrate/17d7d49ad5a0aeab3d26ff07c5799910.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8dd0fd0a6f2589cfeffb153c240c2f56.png)
change master to master_host='192.168.138.100',master_user='xiaoming',master_password='Root@1234',master_log_file='mysql-bin.000001',master_log_pos=439;
![](https://i-blog.csdnimg.cn/blog_migrate/3540bb0d2bac53c5ede53857d7ea8749.png)
后两个要跟这里的对应;
![](https://i-blog.csdnimg.cn/blog_migrate/6046a3b8826c2b6422627b39c8ac4bc2.png)
测试主从复制连接
主库怎么操作,从库也会复制操作;就说明链接成功
![](https://i-blog.csdnimg.cn/blog_migrate/2cace9fac952d3bbbb4d9ceeab1879e7.png)
读写分离案例
背景
![](https://i-blog.csdnimg.cn/blog_migrate/8a3c5a29462a61eb8ee117399f7dd629.png)
sharding-JDBC介绍
![](https://i-blog.csdnimg.cn/blog_migrate/4f3444520743a63794d7dc644d548b7a.png)
入门案例
![](https://i-blog.csdnimg.cn/blog_migrate/7779a3c8206b3ef9183a19a43b7d4132.png)
项目实现读写分离
直接往主库里面导入,从库就会自动复制;
![](https://i-blog.csdnimg.cn/blog_migrate/9d947cc349c9c4226c67fc3fc556a139.png)
![](https://i-blog.csdnimg.cn/blog_migrate/da11c708656e575cdb400c980c8b1934.png)
报错:
create connection SQLException, url: jdbc:mysql://192.168.138.100:3306/reggie?characterEncoding=utf-8, errorCode 0, state 08S01
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
修改两个URL:
url: jdbc:mysql://192.168.138.101:3306/rw?characterEncoding=utf-8&useSSL=false
Nginx
Nginx概述
![](https://i-blog.csdnimg.cn/blog_migrate/3e71d42fdfbcdb37674900fb32f86407.png)
![](https://i-blog.csdnimg.cn/blog_migrate/95e94dc4a5830db27e4d36c71b0f6697.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7e02fbf6035fc0b5f8d2d66fd04b648d.png)
1.5安装wget
![](https://i-blog.csdnimg.cn/blog_migrate/0e48651cfc3c0649fc156fa57a851155.png)
4.5 创建文件夹 :mkdir -p /usr/local/nginx ; 6:先编译然后 install; 安装tree命令查看目录结构
![](https://i-blog.csdnimg.cn/blog_migrate/1c3a3226da15f16f9b991ea6cdca702b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8b9b7eea30b47bb2b7f6233c01aae47f.png)
Nginx命令
查看版本
要先cd /usr/local/nginx/sbin
![](https://i-blog.csdnimg.cn/blog_migrate/ceb469bcd739b6c1e50638712ec21286.png)
检查配置文件正确性
![](https://i-blog.csdnimg.cn/blog_migrate/0a4f412d1bf7bbb70a2debb510cd2586.png)
启动和停止
![](https://i-blog.csdnimg.cn/blog_migrate/ea568677ea4f3476df9c0ea02b0bf5ef.png)
关闭防火墙: systemctl stop firewalld ; 如下页面,访问成功
![](https://i-blog.csdnimg.cn/blog_migrate/5f32f8549a7560f8f17bdd36047d814f.png)
重新加载配置文件
![](https://i-blog.csdnimg.cn/blog_migrate/c9e9382c594d4345349673e7396ef831.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a33e1cca0190a58063b60a8c40ff2648.png)
要写路径启动nginx -->不想写路径;
解决方案:把nginx的二进制文件路径配置到系统的环境变量 实现 nginx 即可启动的效果
vim /etc/profile
PATH=$JAVA_HOME/bin:$PATH # 追加成下面的
PATH=/usr/local/nginx/sbin:$JAVA_HOME/bin:$PATH
source /etc/profile
nginx -s reload # 不报错说明添加环境变量成功
Nginx配置文件结构
全局块、Events块、Http块
![](https://i-blog.csdnimg.cn/blog_migrate/3803f075bffea07f0b2584c405ca592a.png)
Nginx具体应用
部署静态资源
![](https://i-blog.csdnimg.cn/blog_migrate/6e524572ab46e9b379a231418554e24b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8208525c290b7ff0ab7e590f2306b5da.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8d492f0fa8a4baeb98cae3baabaae3cc.png)
部署html成功
反向代理(用的最多)
![](https://i-blog.csdnimg.cn/blog_migrate/1d83c7d176b1fb445259948445142bab.png)
![](https://i-blog.csdnimg.cn/blog_migrate/65e309354e64e458e7eef8dbdd7c17ad.png)
区别:正向代理一般是在客户端设置代理服务器,
反向代理客户端不知道反向代理服务器的存在,客户端只需要访问服务器就会返回相应资源,用户不知道代理服务器存在。
![](https://i-blog.csdnimg.cn/blog_migrate/b7c8ff03654e6a2c410754600d9acb8c.png)
负载均衡
![](https://i-blog.csdnimg.cn/blog_migrate/775063b50f3be0545233d9f8f0945125.png)
![](https://i-blog.csdnimg.cn/blog_migrate/49eb654c9a7d4487509703a5f132a7df.png)
项目优化3—前后端分离开发
![](https://i-blog.csdnimg.cn/blog_migrate/ff8d748573760f49bedfea10bd4b7872.png)
前后端分离开发
介绍
![](https://i-blog.csdnimg.cn/blog_migrate/3bf2d1b0215d26523bdb34382239aedb.png)
开发流程
![](https://i-blog.csdnimg.cn/blog_migrate/abc2e0119f7f0263a78e59e9337fcbf3.png)
前端技术栈
![](https://i-blog.csdnimg.cn/blog_migrate/e322899854a43d605e45c6c234b531d0.png)
Yapi (一种定义接口的Web服务)
![](https://i-blog.csdnimg.cn/blog_migrate/b8144d3a4c9a8168e625996e796501ad.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7143f951f38a50aa00c791598fccab30.png)
YApi-教程
YApi-内网搭建
windows环境下部署YApi
我没有跟 原因是需要mangoDB
Swagger
介绍
![](https://i-blog.csdnimg.cn/blog_migrate/cc774d81c73c5fe4c745cadf3d8f9ccf.png)
使用方式
![](https://i-blog.csdnimg.cn/blog_migrate/9e40ddf549da5535008df801a137b32a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8d35fb12c050e6d3231eb195a6d82c64.png)
反正就是maven依赖冲突 卡了三个小时还是解决不了 先往后走吧 主要是knife4j的Swagger框架pom.xml导不进去;
![](https://i-blog.csdnimg.cn/blog_migrate/458370c468a788f3dd5f3f90fc29681e.png)
com.google.guava:guava:jar:27.0.1-android failed to transfer from https://repo.maven.apache.org/maven2 during a previous attempt. This failure was cached in the local repository and resolution is not reattempted until the update interval of central has elapsed or updates are forced. Original error: Could not transfer artifact com.google.guava:guava:jar:27.0.1-android from/to central (https://repo.maven.apache.org/maven2): transfer failed for https://repo.maven.apache.org/maven2/com/google/guava/guava/27.0.1-android/guava-27.0.1-android.jar
Try to run Maven import with -U flag (force update snapshots)
https://blog.csdn.net/yiguoxiaohai/article/details/125708088
几个月之后 ,当我又需要这个项目的时候又可以了 ,我想了一下 之前也总遇到导包不成功问题,有一次 我发现之前没设置Maven自动导包,后来设置上了;就导包成功了,可能是因为这个,又成了
![](https://i-blog.csdnimg.cn/blog_migrate/1fb8bc7f842c0145b0bf0b638fd3e3d7.png)
因为两个数据库对的 所以我觉得就是我第二个虚拟机的mysql的版本不对 瑞吉的mysql是5.几
![](https://i-blog.csdnimg.cn/blog_migrate/d2dabe33e070eb2f65ec38a69d3417ed.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d26d6471b0772472967b7739d56f04b9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/52f60978201088e23a74ac0df6084c71.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ca942910bf633e9cc4f263befa7f7a2b.png)
常用注解
![](https://i-blog.csdnimg.cn/blog_migrate/9588a277a9f5985bfa6ce2aab321abd8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/76dbbec68256251107f4df3a97936cc5.png)
项目部署
部署架构
![](https://i-blog.csdnimg.cn/blog_migrate/615c7cb71f729b55de2efda348e93016.png)
部署环境说明
![](https://i-blog.csdnimg.cn/blog_migrate/091845f7640e02aee6574266847ec54f.png)
麻烦没搞
部署前端项目
![](https://i-blog.csdnimg.cn/blog_migrate/08547e4bb04db40fd1a2d1c862647ff5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a242dfc164990872aefcc0792bf3aa7f.png)
部署后端项目
![](https://i-blog.csdnimg.cn/blog_migrate/3d8b29dd272567d9d0b197e2edd77b93.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d150930ebfbc025f1bd61928297831ef.png)
![](https://i-blog.csdnimg.cn/blog_migrate/42580dce84325ef0f4e912773d95601e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/40344e1641b4d8d9afb742370377d7ad.png)
完结撒花!