苍穹外卖Day-5
一、业务代码-修改菜单
第一个接口:根据id查询dish
返回对应的VO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishVO implements Serializable {
private Long id;
//菜品名称
private String name;
//菜品分类id
private Long categoryId;
//菜品价格
private BigDecimal price;
//图片
private String image;
//描述信息
private String description;
//0 停售 1 起售
private Integer status;
//更新时间
private LocalDateTime updateTime;
//分类名称
private String categoryName;
//菜品关联的口味
private List<DishFlavor> flavors = new ArrayList<>();
//private Integer copies;
}
三层架构代码:
/**
* 根据id查询菜品
* @param id
* @return
*/
@ApiOperation("根据id查询菜品")
@GetMapping("/{id}")
public Result<DishVO> getById(@PathVariable Long id){
log.info("根据id查询菜品:{}",id);
DishVO dishVO = dishService.getByIdWithFlavor(id);
return Result.success(dishVO);
}
//根据id查询菜品
DishVO getByIdWithFlavor(Long id);
/**
* 根据id查询菜品
* @param id
* @return
*/
public DishVO getByIdWithFlavor(Long id) {
//查询Dish
Dish dish = dishMapper.getById(id);
//查询DishFlavor
List<DishFlavor> flavors = dishFlavourMapper.getByDishId(id);
DishVO dishVO = new DishVO();
BeanUtils.copyProperties(dish,dishVO);
dishVO.setFlavors(flavors);
//封装数据
return dishVO;
}
/**
* 根据菜品id查询口味数据
* @param id
* @return
*/
@Select("select * from dish_flavor where dish_id = #{id}")
List<DishFlavor> getByDishId(Long id);
接口二:修改菜单
三层架构:
/**
* 更新菜品
* @return
*/
@ApiOperation("更新菜品")
@PutMapping
public Result update(@RequestBody DishDTO dishDTO){
log.info("更新菜品:{}",dishDTO);
dishService.updateWithFlavor(dishDTO);
return Result.success();
}
//更新菜品
void updateWithFlavor(DishDTO dishDTO);
/**
* 更新菜品
* @param dishDTO
*/
public void updateWithFlavor(DishDTO dishDTO) {
// 由于口味的状态太多,可能修改,可能不该,也可能删除,所以直接先删除再插入
//修改菜品表的基本信息
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
dishMapper.update(dish);
//删除口味表的数据
dishFlavourMapper.deleteByDishIds(dish.getId());
//插入新的口味数据
List<DishFlavor> flavors = dishDTO.getFlavors();
if(flavors!=null && flavors.size()>0){
for (DishFlavor flavor : flavors) {
flavor.setDishId(dish.getId());
}
//批量插入
dishFlavourMapper.insertBatch(flavors);
}
}
/**
* 更新菜品数据
* @param dish
*/
@AutoFill(OperationType.UPDATE)
void update(Dish dish);
<!-- 更新菜品-->
<update id="update">
update sky_take_out.dish
<set>
<if test="name != null">
name = #{name},
</if>
<if test="categoryId != null">
category_id = #{categoryId},
</if>
<if test="price != null">
price = #{price},
</if>
<if test="image != null">
image = #{image},
</if>
<if test="description != null">
description = #{description},
</if>
<if test="updateTime != null">
update_time = #{updateTime},
</if>
<if test="updateUser != null">
update_user = #{updateUser},
</if>
</set>
<where> id = #{id}</where>
</update>
二、Redis使用
五种Redis基本数据类型
Redis基本数据类型的操作·
字符串类型(String)
set key value --创建字符串
get key -- 获取字符串
incr key -- 返回自加1后的值(注意:只有key对应的value是数字的时候才能进行自加,否则会报错)
decr key-- 返回自减少1后的值(注意:只有key对应的value是数字的时候才能进行自加,否则会报错)
INCRBY KEY NUMBER --指定增加某个数
DECRBY KEY NUMBER --指定减少某个数
INCRFLOAT KEY NUMBER --增加float型的数
2.哈希类型(Hash)
一.创建哈希
HSET KEY FILED VALUES --单个创建
HMSET KEY FILED1 VALUES1 FILED2 VALUES2... -- 多个创建
二.获取哈希某个属性的值
HGET KEY FILED --单个获取
HMGET KEY FILED1 FILED2 ...--多个获取
HGETALL KEY -获取目标key下所有的filed与对应的值
三.检测是否存在
HEXIST KEY FILED
--检测想要的key对象有没有某个方面比如CAR想要看有没有价格方面信息等等HSETNX KEY FILED VALUE-- hsetnx(hset if not exist)
如果不存在某个方面的情况下,设置某个方面与其值,比如我设置CAR price 10 可以知道 CAR 已经存在对应的price,并且返回值是(integer)0意味着创建失败四.哈希键的修改
1.HINCRBY FILED VALUES +/-NUMBER--
修改某个方面的值比如price ,并且通过值得正负实现是增还是减2.HDEL key FILED/ FILED2 /FILED3 ...--删除某个方面
五.其它
HKEYS KEY --获取某个键对应的所有方面(域),比如CAR 能获取到其price name 等等
HVALS KEY --与hkeys不同的是,hvals key获取的是某个键对应所有方面的值
HLEN KEY -- 计算并返回某个键名对应有多少个域filed
3.列表类型(list)
一、修改
LPUSH key --即是left push 从左插入,并返回当前列表内元素的个数RPUSH key --对应的是right push 从右边插入,并返回当前列表内元素的个数
LPOP key --输出列表最左边的值,再将其删除
RPOP key --输出列表最右边的值,再将其删除
LREM key count value --从列表中删除指定数量的匹配元素。
注意LREM key count value中
如果count>0 则意味着从左到右开始删除count 个值为 value的元素
如果count<0 则意味着从右到左开始删除count 个值为 value的元素
二.获取列表以及相关信息LRANGE KEY 0 -1 --表示从第0个开始,-1(表示全部)输出(从左到右输出)
LRANGE KEY 0 2 --表示从第0个开始,到第2个输出
LINDEX KEY index --获取列表中指定索引位置的元素。
LLEN KEY --获取列表的长度(元素个数)
三、列表的修改
LINSERT key BEFORE|AFTER pivot value --将值插入到列表中某个元素之前或之后
4.集合类型(set)
1.集合的创建删除与查询
SADD KEY MEMBER / MEMBER1 / MEMBER2...--创建集合
SREM KEY MEMBER / MEMBER1 / MEMBER2... --删除集合key中的成员
SMEMBERS KEY --查看集合内key所有的成员
SISMEMBER KEY MEMBER --集合中是否存在
SDIFF KEY1 KEY2 --集合相减,举例有两个社团key1 和key2那么当需求社团成员只报了key1社团没有报社团key2时候就可以用集合相减
SINTER KEY1 KEY2--集合取交集
还是如上的例子,当我需要即报名了key1社团和key2社团的成员时候就可以用SINTER KEY1 KEY2 来计算
SUNION KEY1 KEY2--集合取并集
5.有序集合类型(Zset)
一.有序集合的创建与查询
ZADD KEY SCORE MEMBER/ SCORE MEMBER 2 ...--创建有序集合
ZSCORE MEMBER --查询某个成员的分数
ZRANGE KEY START END [withscores]--从低到高进行排名 当加上with scores后缀时候不止有对应的成员排名,还会展示成员的分数
ZREVRABFE KEY START END [withscores]--reverse zrange 即对zrange命令的反转就是从高到低进行排名
ZCARD KEY --统计zset内成员的个数
ZCOUNT KEY MIN MAX --统计集合key成员中分数段为[min,max]的成员个数
ZRANK KEY MEMBER --查询某个成员在集合内排第几名
二、有序集合的修改
ZINCRBY KEY INCRESEMENT MEMBER-- 对集合key内成员的score进行操作可以通过最后INCRESEMENT 的+ - 进行增加或者减少ZREM KEY MEMBER1 / MEMBER2...--删除集合key中的成员member1
通过RAM对Redis数据库进行操作
先下载redis,之后在文件夹中找到两个核心程序
下载RAM
通过RAM进行配置
通过Java代码对Redis数据库进行操作
1.导入Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置数据源:
spring:
redis:
host: localhost
port: 6379
password: 123456
database: 10
3.设置配置类
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisCollectionFactory){
log.info("开始创造redisTemplate模板");
//创建模板对象
RedisTemplate redisTemplate = new RedisTemplate();
//设置连接工厂
redisTemplate.setConnectionFactory(redisCollectionFactory);
//设置key序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
通过Java使用Redis实现对五种数据类型的操作案例:
1.创建测试类(需要与项目com.sky同级)
2.创建对应的操作类
@Test
public void test(){
//操作字符串
ValueOperations valueOperations = redisTemplate.opsForValue();
//操作hash
HashOperations hashOperations = redisTemplate.opsForHash();
//操作list
ListOperations listOperations = redisTemplate.opsForList();
//操作set
SetOperations setOperations = redisTemplate.opsForSet();
//操作zset
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}
通过Java对Redis的常用类型的操作
一、操作字符串类型
/**
* 测试字符串
*/
@Test
public void ValueTest(){
//redis操作字符串常用方法有:set get setIfAbsent setIfPresent setIfAbsent setIfPresent getAndSet
//1.操作字符串
ValueOperations valueOperations = redisTemplate.opsForValue();
//设置key-value
valueOperations.set("name","AlphaMilk");
//获取key的value
String name = (String) valueOperations.get("name");
System.out.println(name);
//设置key-value并设置过期时间
valueOperations.set("age",14,13, TimeUnit.MINUTES);
//获取key的value和过期时间
valueOperations.get("age");
//设置并判断key-value是否存在
valueOperations.setIfAbsent("code","1");
valueOperations.setIfAbsent("code","2");
}
二.操作hash类型
/**
* 测试hash
*/@Test
public void HashTest(){
//常用方法有:put get delete hasKey keys values entries
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("user","name","AlphaMilk");
hashOperations.put("user","age","14");
//获取hash的key的value
String name = (String) hashOperations.get("user","name");
System.out.println(name);
//获取hash的所有key
Set user = hashOperations.keys("user");
System.out.println(user);
//获取hash的所有value
List list = hashOperations.values("user");
System.out.println(list);
//删除hash的key
hashOperations.delete("user","name");
}
三.操作list类型
@Test
public void ListTest(){
//常用方法有:leftPush leftPushAll rightPush rightPushAll leftPop rightPop index range trim
ListOperations listOperations = redisTemplate.opsForList();
//左插入
listOperations.leftPush("list","1");
//右插入
listOperations.rightPush("list","2");
//多个左插入
listOperations.leftPushAll("list","3","4","5");
//多个右插入
listOperations.rightPushAll("list","6","7","8");
//左弹出
String list1 = (String) listOperations.leftPop("list");
System.out.println(list1);
//右弹出
String list2 = (String) listOperations.rightPop("list");
System.out.println(list2);
//获取指定位置的元素
String value = (String) listOperations.index("list",1);
System.out.println(value);
//获取指定范围的元素
List list = listOperations.range("list",0,2);
System.out.println(list);
//修剪
listOperations.trim("list",1,2);
}
四、操作set类型
@Test
public void SetTest(){
//常用方法有:add remove members size isMember intersect union difference
SetOperations setOperations = redisTemplate.opsForSet();
//添加元素
setOperations.add("set1","1","2","3","4","5");
//获取元素
Set set1 = setOperations.members("set1");
System.out.println(set1);
//获取元素个数
Long size = setOperations.size("set1");
System.out.println(size);
//删除元素
setOperations.remove("set1","1");
//判断元素是否存在
Boolean isMember = setOperations.isMember("set1","1");
System.out.println(isMember);
//创建集合set2
setOperations.add("set2","3","4","5","6","7");
//求交集
Set intersect = setOperations.intersect("set","set2");
System.out.println(intersect);
//求并集
Set union = setOperations.union("set1","set2");
System.out.println(union);
//求差集
Set difference = setOperations.difference("set1","set2");
System.out.println(difference);
}
五、操作ZSet类型
@Test
public void ZSetTest(){
//常用方法有:add remove range rangeByScore size rank reverseRank incrementScore
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//添加元素
zSetOperations.add("zset","1",1);
//获取元素
Set zset = zSetOperations.range("zset",0,1);
System.out.println(zset);
//获取元素个数
Long size = zSetOperations.size("zset");
System.out.println(size);
//删除元素
zSetOperations.remove("zset","1");
//添加多个元素
zSetOperations.add("zset","2",2);
zSetOperations.add("zset","3",3);
zSetOperations.add("zset","4",4);
//获取指定范围的元素
Set zset1 = zSetOperations.range("zset",0,2);
System.out.println(zset1);
//获取排名
Long rank = zSetOperations.rank("zset","2");
System.out.println(rank);
//获取倒序排名
Long reverseRank = zSetOperations.reverseRank("zset","2");
System.out.println(reverseRank);
//增加分数
Double incrementScore = zSetOperations.incrementScore("zset","2",2);
System.out.println(incrementScore);
//获取指定分数范围的元素
Set zset2 = zSetOperations.rangeByScore("zset",2,4);
System.out.println(zset2);
}
六、通用命令
@Test
public void CommonTest(){
//通用命令常用方法有:delete expire expireAt hasKey keys
//删除key
redisTemplate.delete("name");
//设置过期时间
redisTemplate.expire("age",10,TimeUnit.SECONDS);
//设置过期时间
redisTemplate.expireAt("age", Instant.ofEpochSecond(10));
//判断key是否存在
Boolean hasKey = redisTemplate.hasKey("age");
System.out.println(hasKey);
//获取所有key
Set keys = redisTemplate.keys("*");
System.out.println(keys);
}
实战:店铺状态设置
为什么要通过Redis设置?因为存储店铺状态如果用到mysql,那么需要一张单独的表去存放,过于浪费并且效率不高。
代码实现:
1.实现管理端的设置状态与获取状态
//关键一步:因为同时存在用户端和管理端,需要通过Bean
名称区别开来,否则会报错,出现同个shopController冲突
@RestController("adminShopController")
@Slf4j
@RequestMapping("/admin/shop")
@Api(tags = "店铺设置")
public class ShopController {
private static final String SHOP_STATUS = "SHOP_STATUS";
//引入redisTemplate
@Autowired
private RedisTemplate redisTemplate;
/**
* 管理端的设置店铺状态
* @return
*/
@PutMapping("/{status}")
@ApiOperation("修改店铺状态")
public Result setStatus(@PathVariable Integer status){
log.info("设置店铺状态为{}",status==1?"营业中":"打样中");
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set(SHOP_STATUS,status);
return Result.success();
}
/**
* 获取店铺状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺状态")
public Result getStatus(){
Integer status = (Integer) redisTemplate.opsForValue().get(SHOP_STATUS);
log.info("当前店铺状态为{}",status==1?"营业中":"打样中");
return Result.success(status);
}
}
2.设置用户端的查询店铺状态
@RestController("userShopController")
@Slf4j
@RequestMapping("/user/shop")
@Api(tags = "用户端店铺使用")
public class ShopController {
private static final String SHOP_STATUS = "SHOP_STATUS";
//引入redisTemplate
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取店铺状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺状态")
public Result getStatus(){
Integer status = (Integer) redisTemplate.opsForValue().get(SHOP_STATUS);
log.info("当前店铺状态为{}",status==1?"营业中":"打样中");
return Result.success(status);
}
}
2.设置用户端的查询店铺状态
@RestController("userShopController")
@Slf4j
@RequestMapping("/user/shop")
@Api(tags = "用户端店铺使用")
public class ShopController {
private static final String SHOP_STATUS = "SHOP_STATUS";
//引入redisTemplate
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取店铺状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺状态")
public Result getStatus(){
Integer status = (Integer) redisTemplate.opsForValue().get(SHOP_STATUS);
log.info("当前店铺状态为{}",status==1?"营业中":"打样中");
return Result.success(status);
}
}
为了解决混用问题,需要进行创建多个Docket
三、使用HttpClient进行调用其他服务器接口
当一个项目足够大的时候,一台服务器的性能可能无法支撑这个项目的正常运转。这时候就需要进行多个服务器来共同承载一个项目。而服务器之间的通信就需要依赖像HttpClient,Okhttp这类的客户端编程工具包
使用案例:
导入maven坐标
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
发送Get请求
/*
*通过测试类测试HttpClient发送get请求
*/@Test
public void testGet() throws IOException {
//1.创建HttpClient对象
CloseableHttpClient client = HttpClients.createDefault();
//2.创建HttpGet对象
HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
//3.发送请求
CloseableHttpResponse response = client.execute(httpGet);
//4.获取状态码 与数据
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("状态码为"+statusCode);
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity);
System.out.println("数据为"+string);
//5.关闭资源
response.close();
client.close();
}
发送POST请求
/*
*通过测试类测试HttpClient发送post请求
*/@Test
public void POSTTest() throws IOException {
//1.创建HttpClient对象
CloseableHttpClient client = HttpClients.createDefault();
//2.创建HttpPost对象
HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
//通过fastjson对Json数据格式封装
JSONObject jsonObject = new JSONObject();
jsonObject.put("username","admin");
jsonObject.put("password","123456");
StringEntity entity = new StringEntity(jsonObject.toString());
//指定请求编码方式
entity.setContentEncoding("UTF-8");
//数据格式
entity.setContentType("application/json");
httpPost.setEntity(entity);
//3.发送请求
CloseableHttpResponse response = client.execute(httpPost);
//4.获取状态码 与数据
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("状态码为"+statusCode);
HttpEntity entity1 = response.getEntity();
String string = EntityUtils.toString(entity1);
System.out.println("数据为"+string);
//5.关闭资源
response.close();
client.close();
}
这里可以看到,使用HttpClient有点复杂,所以可以借助以下工具类进行简化操作
/**
* Http工具类
*/
public class HttpClientUtil {
static final int TIMEOUT_MSEC = 5 * 1000;
/**
* 发送GET方式请求
* @param url
* @param paramMap
* @return
*/
public static String doGet(String url,Map<String,String> paramMap){
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
String result = "";
CloseableHttpResponse response = null;
try{
URIBuilder builder = new URIBuilder(url);
if(paramMap != null){
for (String key : paramMap.keySet()) {
builder.addParameter(key,paramMap.get(key));
}
}
URI uri = builder.build();
//创建GET请求
HttpGet httpGet = new HttpGet(uri);
//发送请求
response = httpClient.execute(httpGet);
//判断响应状态
if(response.getStatusLine().getStatusCode() == 200){
result = EntityUtils.toString(response.getEntity(),"UTF-8");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
response.close();
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/ public static String doPost(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (paramMap != null) {
List<NameValuePair> paramList = new ArrayList();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/ public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
if (paramMap != null) {
//构造json格式数据
JSONObject jsonObject = new JSONObject();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
jsonObject.put(param.getKey(),param.getValue());
}
StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");
//设置请求编码
entity.setContentEncoding("utf-8");
//设置数据类型
entity.setContentType("application/json");
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
private static RequestConfig builderRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(TIMEOUT_MSEC)
.setConnectionRequestTimeout(TIMEOUT_MSEC)
.setSocketTimeout(TIMEOUT_MSEC).build();
}
}
四、微信小程序开发
注册
注册后下载微信开发者工具
微信开发者工具介绍
微信开发者工具入门案例开发
动态信息展示:
<!--index.wxml-->
<view class="container">
<view>
{{msg}}
</view>
</view>
// index.js
// const app = getApp()
Page({
data:{
msg: 'xxxxx'
}
});
wxml:模拟按钮获取用户信息
<view>
<button type="primary" bindtap="getUserInfo">获取用户信息</button>
<view>
昵称: {{nickName}}
</view>
<!-- 图像 -->
<image src="{{url}}"></image>
</view>
js:
//获取用户信息
getUserInfo(){
wx.getUserProfile({
desc: '获取用户信息',
sucess:(res)=>{
console.log(res.userInfo)
//为数据赋值
nickName: res.userInfo.nickName
url: res.userInfo.avataUrl
}
})}
获取用户code
wxml
<!-- 微信登陆 -->
<view>
<button type="warn" bindtap="wxLogin">微信登陆</button>
授权码:{{code}}
</view>
JS
Page({
data:{
msg: 'xxxx',
nickName: '',
url: '',
code: ''
},
//获取授权码
wxLogin(){
wx.login({
success : (res)=>{
console.log(res.code)
}
})
}
展示商品:通过异步请求获取商品信息
wxml:
<view>
<button type="default" bindtap="sendRequest">发送请求</button>
</view>
JS:
//发送请求
sendRequest(){
wx.request({
url: 'http://localhost:8080/user/shop/status',
method:'Get',
success: (res)=>{
console.log(res.data)
}
})
}
实现微信登陆:
1.导入小程序代码
2.实现微信登陆
1.登陆流程
## 调用auth.code2Session方式
HTTPS 调用
GET https://api.weixin.qq.com/sns/jscode2session
配置wechat所需要的配置项
application.yml中jwt配置
sky:
jwt:
# 设置jwt签名加密时使用的秘钥
admin-secret-key: itcast
# 设置jwt过期时间
admin-ttl: 7200000
# 设置前端传递过来的令牌名称
admin-token-name: token
# 设置用户的jwt签名加密时使用的秘钥
user-secret-key: alphaMilk
# 设置用户的jwt过期时间
user-ttl: 7200000
# 设置用户的jwt签名加密时使用的秘钥
user-token-name: authentication
yml
#配置Wechat
wechat:
appid: XXXXX
secret: XXXXXX
用户端登陆请求对应的DTO:
/**
* C端用户登录
*/
@Data
public class UserLoginDTO implements Serializable {
private String code;
}
用户端返回对应的VO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {
private Long id;
private String openid;
private String token;
}
Controller:
/**
* 用户登录
*
* @param userLoginDTO 用户登录信息
* @return 用户登录信息
*/
@ApiOperation(value = "用户登录")
@PostMapping("/login")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) {
log.info("用户登录,参数:{}", userLoginDTO);
// 微信用户登录
User user = userService.wxlogin(userLoginDTO);
// 返回用户JWT
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID, user.getId());
String jwt = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);
//封装VO对象
UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(jwt)
.build();
return Result.success(userLoginVO);
}
Service:
public interface UserService {
/**
* 用户登录
*
* @param userLoginDTO 用户登录信息
* @return 用户信息
*/
User wxlogin(UserLoginDTO userLoginDTO);
}
ServiceImpl:
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
//微信接口url
public static final String WX_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
WeChatProperties weChatProperties;
/**
* WX用户登录
*
* @param userLoginDTO 用户登录信息
* @return 用户信息
*/
public User wxlogin(UserLoginDTO userLoginDTO) {
//1.调用wx接口服务获取openid
String openid = getOpenId(userLoginDTO.getCode());
//2.判断是否获取到openid 如果是空抛出异常
if (openid == null) {
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
//3.判断是否是新用户,如果是新用户,保存用户信息
User user = userMapper.getUserByOpenid(openid);
if (user == null){
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.insert(user);
}
//4.返回用户信息
return user;
}
private String getOpenId(String code){
//1.调用wx接口服务获取openid
Map<String, String> map = new HashMap<>();
map.put("appid", weChatProperties.getAppid());
map.put("secret", weChatProperties.getSecret());
map.put("js_code", code);
map.put("grant_type", "authorization_code");
String string = HttpClientUtil.doGet(WX_LOGIN_URL, map);
JSONObject jsonObject = JSON.parseObject(string);
String openid = (String) jsonObject.get("openid");
return openid;
}
}
mapper:
@Mapper
public interface UserMapper {
/**
* 插入用户
*
* @param user 用户
*/
void insert(User user);
/**
* 根据openid查询用户
*
* @param openid openid
* @return 用户
*/
@Select("select * from user where openid = #{openid}")
User getUserByOpenid(String openid);
}
xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.UserMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into sky_take_out.user(openid, name, phone, sex, id_number, avatar, create_time)
VALUES (#{openid},#{name},#{phone},#{sex},#{idNumber},#{avatar},#{createTime}) </insert>
</mapper>
小程序用户端拦截器
通过资源类快速导入jwt相关信息:
@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {
/**
* 管理端员工生成jwt令牌相关配置
*/
private String adminSecretKey;
private long adminTtl;
private String adminTokenName;
/**
* 用户端微信用户生成jwt令牌相关配置
*/
private String userSecretKey;
private long userTtl;
private String userTokenName;
}
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {
@Autowired
private 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.getUserTokenName());
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
log.info("当前用户id:", userId);
//通过ThreadLocal保存员工id
BaseContext.setCurrentId(userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}
配置开启拦截器
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Autowired
private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
// 注入用户拦截器
@Autowired
private JwtTokenUserInterceptor jwtTokenUserInterceptor;
/**
* 注册自定义拦截器
*
* @param registry
*/
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
// 管理员拦截器
registry.addInterceptor(jwtTokenAdminInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/employee/login");
// 用户拦截器
registry.addInterceptor(jwtTokenUserInterceptor)
.addPathPatterns("/user/**")
.excludePathPatterns("/user/user/login")
.excludePathPatterns("/user/shop/status");
}
}
导入商品浏览功能
需求分析:
三层架构
/**
* 根据分类id查询菜品
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
List<DishVO> list = dishService.listWithFlavor(dish);
return Result.success(list);
}
//TODO:测试根据分类id查询菜品
List<Dish> queryByCategoryId(Long id);
/**
* 条件查询菜品和口味
* @param dish
* @return
*/
List<DishVO> listWithFlavor(Dish dish);
/**
* 根据分类id查询菜品
* @param categoryId
* @return
*/
public List<Dish> list(Long categoryId) {
Dish dish = Dish.builder()
.categoryId(categoryId)
.status(StatusConstant.ENABLE)
.build();
return dishMapper.list(dish);
}
/**
* 条件查询菜品和口味
* @param dish
* @return
*/
public List<DishVO> listWithFlavor(Dish dish) {
List<Dish> dishList = dishMapper.list(dish);
List<DishVO> dishVOList = new ArrayList<>();
for (Dish d : dishList) {
DishVO dishVO = new DishVO();
BeanUtils.copyProperties(d,dishVO);
//根据菜品id查询对应的口味
List<DishFlavor> flavors = dishFlavourMapper.getByDishId(d.getId());
dishVO.setFlavors(flavors);
dishVOList.add(dishVO);
}
return dishVOList;
}