Spring Boot企业微信点餐系统学习心得
https://github.com/coder-zrl/sell/
在设计websocket的时候应该指定返回特殊代码代表不同含义,这个项目登录的时候订单列表页会提醒
前端有些潦草
用户点餐界面还没做
用户查询订单还没做,但是api已经实现了
session没仔细搞,readis没仔细搞,分布式没仔细搞,因为时间不够了,要复习啦
没创建搜索框
for-each语法
for (int data: res) {
System.out.println(data);
}
创建List,List只是一个接口,不能被实例化
List<int> intList = new ArrayList<>();
创建一个异常类
public class SellException extends RuntimeException{
private Integer code;
public SellException(ResultEnum resultEnum) {
super(resultEnum.getMessage());
this.code = resultEnum.getCode();
}
}
只要不是同一个函数,就能相互调用,甚至重载函数直接return另一个形式
新建一个BigDecimal必须传入数值哦,可以写new BigDecimal(0);或者new BigDecimal(BigInteger.ZERO);
精确的数字BigDecimal做加减乘除都有自己的函数
生成id要加上synchronized关键字,防止多线程导致错位
CollectionUtils.isEmpty(orderDetailList)判断数组是否为空
@Autowired自动注入。测试的时候别忘了对Service的impl加上自动注入,直接new一下是得不到的
@RestController表示这个类是一个Controller,并且可以接收json数据
@RequestMapping修饰Controller类的时候,作用是先创建一个大的路由作为url前缀
@Mapper表示这是一个mapper,即数据库增删改查
@Slf4j安装了lombok插件同时导入了依赖,就不用写logger对象了,直接使用log.info之类的方法
@DynamicUpdate可以实时更新数据库时间,前提是你设置了自动设置时间
@Configuration表示这个类是起配置作用的
@Bean
@JsonProperty(“name”)对VO修饰,表示这个属性名称传到前端是name
@Transactional是给函数加上事务回滚的功能
@Transient注解会在自动匹配字段的时候忽略掉这个属性
@Validated OrderForm orderForm,BindingResult bindingResult可以使用OrderForm这个类传递参数,也可以写成RequestBody
@JsonSerialize(using=xxx.class)可以直接把某个属性转化为想要的类型,例如
https://blog.csdn.net/weixin_40388298/article/details/88634100
@JsonInclude(JsonInclude.Include.NON_NULL),这个类返回给前端的json数据不包含null字段,全局配置直接去配置文件里写jackson de…-pro…-inclusion: not_null
@RunWith(SpringRunner.class)
@SprintBootTest
测试类名
@Test
测试方法
Controller方法中参数添加@RequestBody()使用一个类来接收json数据,就不用一点点写@RequestParams了,注意字段要与json一致
单元测试的时候,直接右键Go to->Test
层级任务
config--配置文件类
controller--对数据的展现,逻辑在service层写,不要写在controller层
service--放置主要逻辑的地方,Seivice层是给Controller层调用的
impl--是service子文件夹,实现service定义的函数
mapper(dao)--对数据库增删改查
pojo--实体类
VO--视图管理器,传数据给前端
DTO--是数据传输对象
utils--工具类
enums--放置枚举类型
exception--放置错误类型
converiter--用于数据转化创建的类
学到了一个新的东西,视图管理器VO,创建一个类,然后声明属性,返回这个类,就构成了json格式
@JsonProperty(“name”)注解,将字符序列化,即传到前端的json字段是name,而不是类中的categoryName属性
VO可以嵌套,就类似json格式数据嵌套,直接把一个VO丢到另一个VO中就欧克
@RestController //可以接收json
@RequestMapping("/buyer/product") //url前缀
public class BuyerProductController {
@GetMapping("/list") //访问http://localhost:8080/sell/buyer/product/list
public ResultVO list(){
ResultVO resultVO = new ResultVO();
ProductVO productVO = new ProductVO();
ProductInfoVO productInfoVO = new ProductInfoVO();
productVO.setProductInfoVOList(Arrays.asList(productInfoVO));
resultVO.setData(Arrays.asList(productVO));
resultVO.setCode(0);
resultVO.setMeg("成功");
return resultVO;
}
}
impl实现Service的接口的时候要加上@Service注解,否则提示找不到Bean
Mybatis一定一定是写无参构造方法!!!即不写构造方法
记得写@Mapper注解
开启数据库命名规范与Java命名规范有两种形式,写配置类或者改配置文件
mybatis:
configuration:
map-underscore-to-camel-case: true
设置id自增,在传数据的时候就不用把id传进去了
mapper的sql必须是函数里的参数,否则找不到
前端传过来商品id和数量,你去数据库查相关商品信息,然后写入订单情况,这是数据库关联的
Gson可以很方便的将字符串与json转换(写个对象去接收json数据)
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
dependency>
lombok插件和依赖,可以加上@Data注解不写getter、setter、tostring还有配置logback不用实例化logger
<dependence>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependence>
BeanUtils.copyProperties(productInfo,productInfoVO);可以复制属性,但是只有同名的属性才能复制,把前面的复制到后面的去。
postman 使用get方法的时候要在Params上加参数,Post方法在Body加参数
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.3version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.7.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-testartifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
有时候配置文件不对也会报错,例如中文全变成了"?",例如时区有问题,遇到问题不要慌,先百度一下
application.yml
spring:
datasource:
username: root
password: 422518
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sell?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=true
mybatis-config.xml
<configuration>
<typeAliases>
<typeAlias type="com.example.demo.pojo.Veg" alias="Veg"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sell?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
configuration>
Mybatis一定一定不要写有参构造方法!!!
Mybatis一定一定要写无参构造方法!!!
mapper的sql一定要是函数里的参数
//对的
@Update("update product_category set category_name=#{id} where category_id=#{id}")
public int updateProductNameById(int id,String name);
//错的,应该是#{id}和#{type}。这么写是取出对象的属性
@Update("update product_category set category_type=#{category_type} where category_id=#{categoryId}")
public int updateProductTypeById(int id,String type);
lombok插件和依赖。可以加上@Data注解不写getter、setter、tostring还有配置logback不用实例化logger
<dependence>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependence>
给pojo,也就是对象加上@DynamicUpdate注解可以实时更新时间(前提是你设置了创建自动生成时间)
使用mysql可以找到包含所有类型的数据或者直接find一个值,需要在pojo加@Entity //数据库映射成对象
pom.xml
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
application.yml
spring:
datasource:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sell?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=true
测试mapper不用写controller,可以使用Test,直接对函数进行传值
for-each语法
for (int data: res) {
System.out.println(data);
}
Mapper忘了加@Mapper注解了
为了接收json格式要在Controller类中加上@RestController注解
可以直接对Controller对象加上@RequestMapping注解作为url前缀
学到了一个新的东西,视图管理器VO,创建一个类,返回这个类,就构成了json格式
@JsonProperty(“name”)注解,将字符序列化,即传到前端的json字段是name,而不是类中的categoryName属性
VO可以嵌套,就类似json格式数据嵌套,直接把一个VO丢到另一个VO中就欧克
@RestController //可以接收json
@RequestMapping("/buyer/product") //url前缀
public class BuyerProductController {
@GetMapping("/list") //访问http://localhost:8080/sell/buyer/product/list
public ResultVO list(){
ResultVO resultVO = new ResultVO();
ProductVO productVO = new ProductVO();
ProductInfoVO productInfoVO = new ProductInfoVO();
productVO.setProductInfoVOList(Arrays.asList(productInfoVO));
resultVO.setData(Arrays.asList(productVO));
resultVO.setCode(0);
resultVO.setMeg("成功");
return resultVO;
}
}
Arrays.asList就可以,但是ArrayList就不可以
为什么List
List
Bean工具包怕,BeanUtils.copyProperties(productInfo,productInfoVO);可以复制属性,但是只有同名的才能复制,把前面的复制到后面的去。!!!但是有个坑,如果写在最后了,有时候会有问题,比如你先给某个属性赋值了,但是使用了这个方法,可能会把原来的值设置为null!!!
Mapper的实体类没有写无参构造方法,Mybatis会报错
impl实现Service的接口的时候要加上@Service注解,否则提示找不到Bean
只要不是同一个函数,就能相互调用,甚至重载函数直接return另一个形式
OrderMasterMapper的查询为null
,这是因为类的构造方法存在两个,一个是空的,一个是有参的,在sql中查询返回结果是这个类对象,无法自动注入值,并且这个注入值是按表的顺序和你参数的顺序一一对应的,可以写非全参的,目前我无法拯救https://www.bilibili.com/video/BV18E411x7ne
有了微信授权才能获取openid,有了openid才能进行后面的操作,对于某个小程序或者公众号,用户的openid是唯一的
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Requesting_an_API_Test_Account.html
appID是公众号的唯一标识符
appsecret是你的密钥
扫描测试号二维码–>找到网页服务–>网页账号–>网页授权获取用户基本信息
去https://natapp.cn/ 购买隧道,推荐VIP_1型,然后注册二级域名,填写到接口测试号的网页授权获取用户基本信息中,我申请的是coder-zrl.nat100.top 2021.12.10到期,配置隧道的二级域名和端口号(8080),下载程序,命令行执行natapp.exe -authtoken=259ca74374ba5aaa
官方文档–>微信网页开发–>网页授权
复制提供的引导链接,写上自己的appid和redirect_uri以及scope
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx6640a7f5b82ebd37&redirect_uri=http://coder-zrl.nat100.top/sell/weixin/auth&response_type=code&scope=snsapi_base&state=123#wechat_redirect
写一个controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/weixin")
public class WeixinController {
@GetMapping("/auth")
public void auth(@RequestParam("code") String code) {
log.info("进入了auth方法");
log.info("【获取code】code={}",code);
}
}
url的参数官方文档都有详细说明
https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx6640a7f5b82ebd37&secret=85cf924b23a717e12caf329150b988c9&code=CODE&grant_type=authorization_code
返回值是json数据,里面包含openid,具体每个变量什么意义官方文档也有说明
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
使用Spring的模板进行url访问,就是在上面的controller再加上下面的代码
我写了一个WeixinDTO和上面的字段一致,用来获取openid
import com.example.sell.dto.WeixinDTO;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 微信端的调试
* @author ZRL
* @date 2020/12/10 1:25
*/
@Slf4j
@RestController
@RequestMapping("/weixin")
public class WeixinController {
@GetMapping("/auth")
public void auth(@RequestParam("code") String code) {
log.info("进入了auth方法");
log.info("【获取code】code={}", code);
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=" + code + "&grant_type=authorization_code";
//Spring提供了一个简单便捷的模板类实现java代码访问restful服务
//get是指调用get方法,object是我们要转化成的类型
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
log.info("response={}",response);
Gson gson = new Gson();
WeixinDTO data = gson.fromJson(response,WeixinDTO.class);
log.info("openid={}",data.getOpenid());
}
}
https://www.bilibili.com/video/BV1pD4y1d7jd
thymeleaf语法
ibootstrap进行拖拽构建网页结构
css放在link,可以不用下载到本地
在我们将数据展示的时候,orderDto的支付状态是代码,我们想变为文字,不应在html里写表达式判断,应该是给类重新创建个方法获取枚举的值
然后就会有问题,好几种状态,难道每个类都要写重复代码吗?其实不需要,我们只需要将枚举继承自同一个接口,接口声明一个方法,然后在Util包中实现它即可
实例化泛型的时候要对他说明,继承自哪里
public static <T extends CodeEnum>T getByCode(Integer code,Class<T> enumClass)
给属性或者方法添加@JsonIgnore可以在返回json对象的时候忽略这个字段
使用到了thymeleaf模板
org.springframework.boot
spring-boot-starter-thymeleaf
使用ibootstrap进行拖拽,生成前端代码,然后copy官网的对应版本的bootstrap.min.css链接到head中的link链接中,也可以直接复制link标签,奥,原来css可以不用下载啊!真是舒服!!!但是可能会导致范围跟速度很慢呢,而且必须有网络
,然后再修改一下网页结构当参数不是必填的时候可以这样写
@RequestParam(value = "productId",required = false)
新增和编辑可以使用一套模板
<script>
var websocket=null;
//判断浏览器是否支持
if('Websocket' in window) {
websocket = new WebSocket('ws://localhost:8080/sell/websocket');
} else {
alert('该浏览器不支持websocket');
}
websocket.onopen = function (event) {
console.log('建立连接');
}
websocket.onclose = function (event) {
console.log('连接关闭');
}
websocket.onmessage = function (event) {
console.log('收到消息:'+event.data);
//弹窗提醒,播放音乐
}
websocket.onerror = function (event) {
alert('webcoket通信发生错误');
}
websocket.onbeforeload = function () {
websocket.close();
}
script>
依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
写一个配置类
@Component
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
java
@Component
@ServerEndpoint("/websocket")
@Slf4j
public class Websocket {
private Session session;
// 存储websocket
private static CopyOnWriteArraySet<Websocket> webSocketSet = new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this);
log.info("有新的客户端连接,当前总数为:{}",webSocketSet.size());
}
@OnClose
public void onClose() {
webSocketSet.remove(this);
log.info("客户端关闭,当前总数为:{}",webSocketSet.size());
}
@OnMessage
public void onMessage(String message) {
log.info("【收到消息:{}】",message);
}
public void sendMessage(String message) {
for(Websocket websocket:webSocketSet) {
log.info("【websocket广播消息,message={}】",message);
try {
websocket.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
然后自动注入到别的impl中
创建一个handler文件夹,里面放处理异常的handler