豆瓣书评网项目
环境配置
1.springmvc 搭建流程
pom.xml :
<!--1.Maven依赖spring-webmvc-->
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
web.xml:
<!--2.配置DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext*.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
applicationcontent.xml:
<!--3.开启SpringMVC注解模式-->
<context:component-scan base-package="com.imooc"/><!--扫描组件-->
<mvc:annotation-driven>
</mvc:annotation-driven>
<mvc:default-servlet-handler/>
web.xml
<!-- 4.解决中文乱码 -->
<filter>
<filter-name>characterFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在<mvc:annotation-driven>标签下添加
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=utf-8</value>
<!--5.JSON(Jackson)序列化输出配置 -->
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
2.freemarker搭建
<!--1.Maven依赖-->
<!--Freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
applicationContext.xml
<!-- 配置Freemarker模板引擎 -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/ftl"/>
<property name="freemarkerSettings">
<props>
<prop key="defaultEncoding">UTF-8</prop><!--ajax在读取文件时 -->
</props>
</property>
</bean>
<bean id="ViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="contentType" value="text/html;charset=utf-8"/><!--渲染后响应输出时 -->
<property name="suffix" value=".ftl"/>
</bean>
3.Mybatis整合
<!--Mybatis整合步骤: 1.引入依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- MyBatis与Spring整合组件 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.14</version>
</dependency>
<!-- MyBatis与Spring的整合配置 -->
applicationContext.xml
<!--2.配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/imooc_reader?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="20"/>
</bean>
<!-- 3. SqlSessionFactoryBean用于根据配置信息创建SqlSessionFactory,不再需要我们自己编码创建 -->
<!-- 原生Mybaits与Spring整合
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
-->
<bean id="sessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
<!-- 5.MyBatis配置文件地址 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--4.配置Mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.imooc.reader.mapper"/>
</bean>
4.junit
<!-- 单元测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
5.logback
<!-- logback日志组件 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
创建logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{HH:mm:ss} %-5level [%thread] %logger{30} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="console"/>
</root>
</configuration>
6.声明式事务
applicationcontext.xml
<!--声明式事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
7.MyBatis-Plus
1.添加依赖
<!-- MyBatis-Plus依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.3.2</version>
</dependency>
2.在applicationContext.xml的sessionFactory处替换
<!-- <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
<bean id="sessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
3.添加分页组件 在mybatis-config.xml中添加
<configuration>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/>
</plugins>
</configuration>
4.MyBatis-Plus核心注解
@TableName - 将实体类与表名映射
@TableId - 说明对应属性是表的主键
@TableField - 设置属性与列名的对应关系
与字段名相同时或符合驼峰命名规则时(book_name-----bookName)属性可省略
5.使用步骤
1.创建实体类,基于@Table/@TableId/@TableField进行自动映射
2.创建Mapper接口继承BaseMapper,创建Mapper XML
3.开发时注入Mapper对象,通过内置API实现CRUD操作
@TableName("test") //说明实体对应哪一张表
public class Test {
@TableId(type = IdType.AUTO)
@TableField("id") //说明属性对应哪个字段
private Integer id;
@TableField("content") //如果字段名与属性名相同或者符合驼峰命名转换规则,则TableField可省略
private String content;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MyBatisPlusTest {
@Resource
private TestMapper testMapper;
@org.junit.Test
public void testInsert(){
Test test = new Test();
test.setContent("MyBatis Plus测试");
testMapper.insert(test);
}
@org.junit.Test
public void testUpdate(){
Test test = testMapper.selectById(9);
test.setContent("MyBatis Plus测试1");
testMapper.updateById(test);
}
@org.junit.Test
public void testDelete(){
testMapper.deleteById(9);
}
@org.junit.Test
public void testSelect(){
QueryWrapper<Test> queryWrapper = new QueryWrapper<Test>();
queryWrapper.eq("id", 7);
queryWrapper.gt("id", 5);
List<Test> list = testMapper.selectList(queryWrapper);
System.out.println(list.get(0));
}
}
6.BaseMapper接口核心AP
| 方法名 | 用途 |
| insert(entity) | 数据新增,自动生成insert sql,根据@TableId决定注解生成方式 |
| updateById(entity) | 根据主键更新对应对象,自动生成update sql |
| deleteById(id) | 根据主键删除数据,自动生成delete sql |
| selectById(id) | 按主键查询对应的实体对象 |
| selectList(queryWrapper) | 根据查询生成器(QueryWrapper)的条件自动生成sql查询返回List集合 |
| selectPage(page,queryWrapper) | 分页查询方法,自动生成分页limit子句,返回IPage分页对象 |
插件使用
Art-Template的使用
1.下载引用art-template.js
2.定义模板
新建一个script代码块,类型为text/html <script type="text/html" id="tpl">
3.把需要渲染的前端代码剪切在代码块中
4.替换 语法{{ 对象的属性名 }}
template("tpl",book);//具体按个模板id 传入的数据
评分插件raty
1.引入css,js
2.指定资源目录
$.fn.raty.defaults.path="./resources/raty/lib/images";
3.设置只读
<span class="stars" data-score="{{evaluationScore}}" title="gorgeous"></span>
$(".stars").raty({readOnly:true});
.
Kaptcha
1.导入依赖
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
2.配置
<bean id="kaptchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
<property name="config">
<bean class="com.google.code.kaptcha.util.Config">
<constructor-arg>
<props>
<prop key="kaptcha.border">no</prop>
<prop key="kaptcha.image.width">120</prop>
<prop key="kaptcha.textproducer.font.color">blue</prop>
<prop key="kaptcha.textproducer.font.size">40</prop>
<prop key="kaptcha.textproducer.char.length">4</prop>
</props>
</constructor-arg>
</bean>
</property>
</bean>
3.controller控制器
package com.imooc.reader.controller;
import com.google.code.kaptcha.Producer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
@Controller
public class KaptchaController {
@Resource
private Producer kaptchaProducer;
@GetMapping("/verify_code")
private void careatVerifyCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setDateHeader("Expires",0);//响应过期时间
//缓存控制 不存储 不缓存 必须重新校验
response.setHeader("Cache-Control","no-store,no-cache,must-revalidate");
response.setHeader("Pragma","no-cache");
response.setContentType("image/png");
String verifyCode = kaptchaProducer.createText();
request.getSession().setAttribute("kaptchaVerifyCode",verifyCode);
System.out.println( request.getSession().getAttribute("kaptchaVerifyCode"));
BufferedImage image = kaptchaProducer.createImage(verifyCode);
ServletOutputStream outputStream = response.getOutputStream();
ImageIO.write(image,"png",outputStream);
outputStream.flush();
outputStream.close();
}
}
前端验证码对比
function reloadVerifyCode(){
$("#imgVerifyCode").attr("src", "/verify_code?ts=" + new Date().getTime());
}
$("#imgVerifyCode").click(function () {
reloadVerifyCode();
});
MD5使用
1.导入依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
2.编写工具类
public class MDUtils {
public static String md5Digest(String source,Integer salt){
char[] ca=source.toCharArray();
for (int i = 0; i < ca.length; i++) {
ca[i]=(char)(ca[i]+salt);
}
String target=new String(ca);
String md5= DigestUtils.md5Hex(target);
return md5;
}
}j
3.使用
Member member=new Member();
member.setUsername(username);
member.setNickname(nickname);
int salt=new Random().nextInt(1000)+1000;
String md5 = MD5Utils.md5Digest(password, salt);
member.setPassword(md5);
member.setSalt(salt);
Spring-Task
1.概念:
定时任务模块:周期后台自动执行任务,可利用Cron表达式实现灵活的定时处理
2.Cron表达式:

秒 0,30 每分的0和30秒
分 0-5 每时的前5分钟
* 所以
? 星期和日 互斥
3.使用步骤
开启注解
<task:annotation-driven/>
编写任务类
@Component
public class ComputerTask {
@Resource
private BookService bookService;
//调度
@Scheduled(cron = "0 * * * * ?")
public void updateEvaluation(){
bookService.updateEvaluation();
System.out.println("已更新所以评分");
}
}
wangeditor
官网:https://www.wangeditor.com/
1.概念Typescript 开发的 Web 富文本编辑器, 轻量、简洁、易用、开源免费
2.使用步骤
//初始化
var E=window.wangEditor;
var editor=new E("#divEditor");
editor.create()
//读取
document.getElementById("btnRead").onclick=function(){
var content = editor.txt.html();
alert(content);
}
//写入
document.getElementById("btnWrite").onclick=function(){
var html="<li style='color:red'>张三<b>德玛</b> </li>";
editor.txt.html(html);
}
jsoup
1.导入依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.2</version>
</dependency>
2.使用
Document doc = Jsoup.parse(html数据);
String cover = doc.select("img").first().attr("src");//标签属性
rawBook.setCover(cover);
springMVC上传文件
1.导入依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2.配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
</bean>
3.上传
@PostMapping("/upload")
@ResponseBody
public Map upload(@RequestParam("img") MultipartFile file, HttpServletRequest request) throws IOException {//img是前端界面设置的文件名
String uploadPath=request.getServletContext().getResource("/").getPath()+"/upload/";//运行时根路径
String fileName=new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
file.transferTo(new File(uploadPath+fileName+suffix));//另存为
Map result=new HashMap();
result.put("errno",0);
result.put("data",new String[]{"/upload/"+fileName+suffix});
return result;
}
实现步骤
导入数据库和所有静态资源
https://files.cnblogs.com/files/blogs/665099/%E8%AE%AD%E7%BB%83%E7%B4%A0%E6%9D%90.rar
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId>
<artifactId>imooc-reader</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- spring-mvc-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.0-rc2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0-rc2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0-rc2</version>
</dependency>
<!--Freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!-- 事务管理-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.9</version>
</dependency>
<!--测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.3.2</version>
</dependency>
<!-- kaptcha-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!-- MD5-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<!-- 文件上传下载-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.2</version>
</dependency>
</dependencies>
</project>
web.xml
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
mybatis.xml
点击查看代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/>
</plugins>
</configuration>
applicationContext.xml
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.imooc.reader"/>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=utf-8</value>
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<mvc:default-servlet-handler/>
<!-- FreeMarkerConfigurer视图解析器配置-->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/ftl"/>
<property name="freemarkerSettings">
<props>
<prop key="defaultEncoding">UTF-8</prop><!--ajax在读取文件时 -->
</props>
</property>
</bean>
<bean id="ViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="contentType" value="text/html;charset=utf-8"/>
<property name="suffix" value=".ftl"/>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/imooc_reader?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="20"/>
</bean>
<!-- SqlSessionFactoryBean用于根据配置信息创建SqlSessionFactory,不再需要我们自己编码创建 -->
<!-- 原生Mybaits与Spring整合
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
-->
<bean id="sessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
<!-- MyBatis配置文件地址 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置Mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.imooc.reader.mapper"/>
</bean>
<!--声明式事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
一.显示图书类别
1.创建实体类 写注解@TableName
点击查看代码
package com.imooc.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("category")
public class Category {
@TableId(type = IdType.AUTO)
private Integer categoryId;
private String categoryName;
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
@Override
public String toString() {
return "Category{" +
"categoryId=" + categoryId +
", categoryName='" + categoryName + '\'' +
'}';
}
}
2.编写接口Mapper
点击查看代码
public interface CategoryMapper extends BaseMapper<Category> {
}
3.写Mapper.xml 写namespace
<?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.imooc.reader.mapper.CategoryMapper">
</mapper>
4.编写service 接口实现类 mapper.selsectList
public interface CategoryService {
public List<Category> selectCategoryList();
}
package com.imooc.reader.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imooc.reader.entity.Category;
import com.imooc.reader.mapper.CategoryMapper;
import com.imooc.reader.service.CategoryService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service("CategoryService")
public class CategoryServiceImp implements CategoryService {
@Resource
private CategoryMapper categoryMapper;
public List<Category> selectCategoryList() {
QueryWrapper<Category> categoryQueryWrapper = new QueryWrapper<Category>();
List<Category> categoryList = categoryMapper.selectList(categoryQueryWrapper);
return categoryList;
}
}
5.测试 Runwith ContextConfiguration 声明接口 调用
点击查看代码
package com.imooc.reader.service.Impl;
import com.imooc.reader.entity.Category;
import com.imooc.reader.service.CategoryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class CategoryServiceImpTest {
@Resource
private CategoryService categoryService;
@Test
public void selectCategoryList() {
List<Category> categories = categoryService.selectCategoryList();
System.out.println(categories);
}
}
6.编写controller
package com.imooc.reader.controller;
import com.imooc.reader.entity.Category;
import com.imooc.reader.service.CategoryService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import java.util.List;
@Controller
public class CategoryController {
@Resource
private CategoryService categoryService;
@GetMapping("/")
@ResponseBody
public ModelAndView showIndex(){
ModelAndView modelAndView=new ModelAndView("/index");
List<Category> categoryList = categoryService.selectCategoryList();
modelAndView.addObject("categoryList",categoryList);
return modelAndView;
}
}
7.前端 <#List <if ${类目.属性}
<#list categoryList as category>
<a style="cursor: pointer" data-category="${category.categoryId}" class="text-black-50 font-weight-bold category">${category.categoryName}</a>
<#if category_has_next >
|
</#if>
</#list>
二.完成图书分页查询
1.创建实体类 写注解@TableName
点击查看代码
package com.imooc.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("book")
public class Book {
@TableId(type = IdType.AUTO)
private Long bookId;
private String bookName;
private String subTitle;
private String author;
private String cover;
private String description;
private Long categoryId;
private Float evaluationScore;
private Integer evaluationQuantity;
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getSubTitle() {
return subTitle;
}
public void setSubTitle(String subTitle) {
this.subTitle = subTitle;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getCover() {
return cover;
}
public void setCover(String cover) {
this.cover = cover;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
public Float getEvaluationScore() {
return evaluationScore;
}
public void setEvaluationScore(Float evaluationScore) {
this.evaluationScore = evaluationScore;
}
public Integer getEvaluationQuantity() {
return evaluationQuantity;
}
public void setEvaluationQuantity(Integer evaluationQuantity) {
this.evaluationQuantity = evaluationQuantity;
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
", subTitle='" + subTitle + '\'' +
", author='" + author + '\'' +
", cover='" + cover + '\'' +
", description='" + description + '\'' +
", categoryId=" + categoryId +
", evaluationScore=" + evaluationScore +
", evaluationQuantity=" + evaluationQuantity +
'}';
}
}
2.编写接口Mapper
public interface BookMapper extends BaseMapper<Book> {
}
3.写Mapper.xml 写namespace
点击查看代码
<?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.imooc.reader.mapper.BookMapper">
</mapper>
4.编写service 接口实现类 mapper.selsectPage
点击查看代码
public interface BookService {
public IPage<Book> selectBookPage(Long categoryId,String order,Integer page,Integer rows);
}
package com.imooc.reader.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imooc.reader.entity.Book;
import com.imooc.reader.mapper.BookMapper;
import com.imooc.reader.service.BookService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("BookService")
public class BookServiceImp implements BookService {
@Resource
private BookMapper bookMapper;
public IPage<Book> selectBookPage(Long categoryId,String order,Integer page, Integer rows) {
if(page==null){
page=1;
}
if(rows==null){
page=10;
}
Page<Book> bookpage=new Page<Book>(page,rows);
QueryWrapper<Book> queryWrapper = new QueryWrapper<Book>();
Page<Book> bookPage = bookMapper.selectPage(bookpage, queryWrapper);
return bookPage;
}
}
5.测试 Runwith ContextConfiguration 声明接口 调用
点击查看代码
package com.imooc.reader.service.Impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.imooc.reader.entity.Book;
import com.imooc.reader.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class BookServiceImpTest {
@Resource
private BookService bookService;
@Test
public void selectBook() {
IPage<Book> bookIPage = bookService.selectBookPage(null,null,2,10);
List<Book> bookList = bookIPage.getRecords();
for (Book book : bookList) {
System.out.println(book.getBookId()+"-----"+book.getBookName());
}
}
}
6.编写controller @requestBody返回json数据
点击查看代码
package com.imooc.reader.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.imooc.reader.entity.Book;
import com.imooc.reader.service.BookService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
@Controller
public class BookController {
@Resource
private BookService bookService;
@GetMapping("/books")
@ResponseBody
public IPage<Book> showBookCategory(Integer p,Long categoryId,String order){
IPage<Book> bookIPage = bookService.selectBookPage(categoryId, order, p, 10);
return bookIPage;
}
}
7.前端 raty初始化 art-template渲染 ajax提交
点击查看代码
<script>
$.fn.raty.defaults.path="./resources/raty/lib/images";
function loadMore(isRest){
if(isRest==true){
$("#nextPage").val(1);
$("#bookList").html("") ;
}
var nextPage = $("#nextPage").val();
var categoryId = $("#categoryId").val();
var order = $("#order").val();
$.ajax({
url:"/books",
type:"get",
data:{p:nextPage,"categoryId":categoryId,"order":order},
dataType:"json",
success:function (json) {
console.log(json);
var records = json.records;
for (let i = 0; i < records.length; i++) {
var book=records[i];
var html=template("art_template",book);
$("#bookList").append(html)
console.log(book);
$(".stars").raty({readOnly:true});
if(json.current<json.pages){
$("#nextPage").val(parseInt(json.current)+1);
$("#btnMore").show();
$("#divNoMore").hide();
}
else {
$("#btnMore").hide();
$("#divNoMore").show();
}
}
}
})
}
$(function () {
loadMore(true);
})
$(function () {
$("#btnMore").click(function () {
loadMore();
})
$(".category").click(function () {
$(".category").removeClass("highlight");
$(".category").addClass("text-black-50");
$(this).addClass("highlight");
var categoryId = $(this).data("category");
$("#categoryId").val(categoryId)
loadMore(true);
})
$(".order").click(function () {
$(".order").removeClass("highlight");
$(".order").addClass("text-black-50");
$(this).addClass("highlight");
var order = $(this).data("order");
$("#order").val(order)
loadMore(true);
})
})
</script>
<script type="text/html" id="art_template">
<a href="/book/{{bookId}}" style="color: inherit">
<div class="row mt-2 book">
<div class="col-4 mb-2 pr-2">
<img class="img-fluid" src="/images/2.jpg">
</div>
<div class="col-8 mb-2 pl-0">
<h5 class="text-truncate">{{bookName}}</h5>
<div class="mb-2 bg-light small p-2 w-100 text-truncate">{{author}}</div>
<div class="mb-2 w-100">{{subTitle}}</div>
<p>
<span class="stars" data-score="{{evaluationScore}}" title="gorgeous"></span>
<span class="mt-2 ml-2">{{evaluationScore}}</span>
<span class="mt-2 ml-2">{{evaluationQuantity}}</span>
</p>
</div>
</div>
</a>
</script>
三.图书详情页
1.前端a标签跳转路径后面包含bookId属性
在bookservice和实现类添加按id查询
点击查看代码
public Book selectBookById(Long bookId);
public Book selectBookById(Long bookId) {
Book book = bookMapper.selectById(bookId);
return book;
}
2.conrtoller
点击查看代码
@GetMapping("/book/{id}")
public ModelAndView showDetail(@PathVariable("id") Long bookId){
Book book = bookService.selectBookById(bookId);
ModelAndView modelAndView=new ModelAndView("/detail");
modelAndView.addObject("book",book);
return modelAndView;
}
3.前端显示
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${book.bookName}</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<link rel="stylesheet" href="/resources/bootstrap/bootstrap.css">
<link rel="stylesheet" href="/resources/raty/lib/jquery.raty.css">
<script src="/resources/jquery.3.3.1.min.js"></script>
<script src="/resources/bootstrap/bootstrap.min.js"></script>
<script src="/resources/art-template.js"></script>
<script src="/resources/raty/lib/jquery.raty.js"></script>
<style>
.container {
padding: 0px;
margin: 0px;
}
.row {
padding: 0px;
margin: 0px;
}
.alert {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.col- * {
padding: 0px;
}
.description p {
text-indent: 2em;
}
.description img {
width: 100%;
}
.highlight {
background-color: lightskyblue !important;
}
</style>
<script>
$.fn.raty.defaults.path = '/resources/raty/lib/images';
$(function () {
$(".stars").raty({readOnly: true});
})
$(function () {
<#if memberReadState??>
$("*[data-read-state='${memberReadState.readState}']").addClass("highlight");
</#if>
<#if !loginMember??>
$("*[data-read-state],#btnEvaluation,*[data-evaluation-id]").click(function () {
$("#exampleModalCenter").modal("show");
})
</#if>
<#if loginMember??>
$("*[data-read-state]").click(function () {
let readState = $(this).data("read-state");
$.post("/update_read_state",{
memberId:${loginMember.memberId},
bookId:${book.bookId},
readState:readState
},function (json) {
if(json.code=="0"){
$("*[data-read-state]").removeClass("highlight");
$("*[data-read-state='" +readState +"']").addClass("highlight");
}
},"json")
})
$("#btnEvaluation").click(function () {
$("#score").raty({})
$("#dlgEvaluation").modal("show")
})
$("#btnSubmit").click(function () {
var score=$("#score").raty("score");
var content=$("#content").val();
if(score==0||$.trim(content)==""){
return;
}
$.post("/evaluate",{
score:score,
bookId:${book.bookId},
memberId:${loginMember.memberId},
content:content
},function (json) {
if(json.code=="0"){
window.location.reload()
}
},"json")
})
$("*[data-evaluation-id]").click(function () {
var evaluationId= $(this).data("evaluation-id");
$.post("/enjoy",{evaluationId:evaluationId},function (json) {
if(json.code=="0"){
$("*[data-evaluation-id='"+evaluationId +"'] span").text(json.evaluate.enjoy);
}
},"json")
})
</#if>
})
</script>
</head>
<body>
<!--<div style="width: 375px;margin-left: auto;margin-right: auto;">-->
<div class="container ">
<nav class="navbar navbar-light bg-white shadow mr-auto">
<ul class="nav">
<li class="nav-item">
<a href="/">
<img src="https://m.imooc.com/static/wap/static/common/img/logo2.png" class="mt-1"
style="width: 100px">
</a>
</li>
</ul>
</nav>
<div class="container mt-2 p-2 m-0" style="background-color:rgb(127, 125, 121)">
<div class="row">
<div class="col-4 mb-2 pl-0 pr-0">
<img style="width: 110px;height: 160px"
src="/images/1.jpg">
</div>
<div class="col-8 pt-2 mb-2 pl-0">
<h6 class="text-white">${book.bookName}</h6>
<div class="p-1 alert alert-warning small" role="alert">
${book.subTitle}
</div>
<p class="mb-1">
<span class="text-white-50 small">${book.author}</span>
</p>
<div class="row pl-1 pr-2">
<div class="col-6 p-1">
<button type="button" data-read-state="1" class="btn btn-light btn-sm w-100">
<img style="width: 1rem;" class="mr-1"
src="https://img3.doubanio.com/f/talion/cf2ab22e9cbc28a2c43de53e39fce7fbc93131d1/pics/card/ic_mark_todo_s.png"/>想看
</button>
</div>
<div class="col-6 p-1">
<button type="button" data-read-state="2" class="btn btn-light btn-sm w-100">
<img style="width: 1rem;" class="mr-1"
src="https://img3.doubanio.com/f/talion/78fc5f5f93ba22451fd7ab36836006cb9cc476ea/pics/card/ic_mark_done_s.png"/>看过
</button>
</div>
</div>
</div>
</div>
<div class="row" style="background-color: rgba(0,0,0,0.1);">
<div class="col-2"><h2 class="text-white">${book.evaluationScore}</h2></div>
<div class="col-5 pt-2">
<span class="stars" data-score="${book.evaluationScore}"></span>
</div>
<div class="col-5 pt-2"><h5 class="text-white">${book.evaluationQuantity}人已评</h5></div>
</div>
</div>
<div class="row p-2 description">
${book.description}
</div>
<div class="alert alert-primary w-100 mt-2" role="alert">短评
<button type="button" id="btnEvaluation" class="btn btn-success btn-sm text-white float-right"
style="margin-top: -3px;">
写短评
</button>
</div>
<div class="reply pl-2 pr-2">
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
您需要登录才可以操作哦~
</div>
<div class="modal-footer">
<a href="/login.html" type="button" class="btn btn-primary">去登录</a>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="dlgEvaluation" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
<h6>${book.bookName}</h6>
<form id="frmEvaluation">
<div class="input-group mt-2 ">
<span id="score"></span>
</div>
<div class="input-group mt-2 ">
<input type="text" id="content" name="content" class="form-control p-4" placeholder="这里输入短评">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="btnSubmit" class="btn btn-primary">提交</button>
</div>
</div>
</div>
</div>
</body>
</html>
四.显示评论列表
1.实体类Evaluation Member
前台页面评论需要会员表的昵称,所以需要Member表的会员数据,Evaluation表中有会员id,通过会员id可以查到会员昵称
点击查看代码
package com.imooc.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
@TableName("evaluation")
public class Evaluation {
@TableId(type = IdType.AUTO)
private Long evaluationId;
private String content;
private Integer score;
private Date createTime;
private Long memberId;
private Long bookId;
private Integer enjoy;
private String state;
private String disableReason;
private Date disableTime;
@TableField(exist = false)
private Book book;
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
@TableField(exist = false)
private Member member;
@TableField(exist = false)
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public Long getEvaluationId() {
return evaluationId;
}
public void setEvaluationId(Long evaluationId) {
this.evaluationId = evaluationId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
public Integer getEnjoy() {
return enjoy;
}
public void setEnjoy(Integer enjoy) {
this.enjoy = enjoy;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getDisableReason() {
return disableReason;
}
public void setDisableReason(String disableReason) {
this.disableReason = disableReason;
}
public Date getDisableTime() {
return disableTime;
}
public void setDisableTime(Date disableTime) {
this.disableTime = disableTime;
}
@Override
public String toString() {
return "Evaluation{" +
"evaluationId=" + evaluationId +
", content='" + content + '\'' +
", score=" + score +
", createTime=" + createTime +
", memberId=" + memberId +
", bookId=" + bookId +
", enjoy=" + enjoy +
", state='" + state + '\'' +
", disableReason='" + disableReason + '\'' +
", disableTime=" + disableTime +
'}';
}
}
点击查看代码
package com.imooc.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
@TableName("`member`")
public class Member {
@TableId(type = IdType.AUTO)
private Long memberId;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
private String username;
private String password;
private Integer salt;
private String nickname;
private Date createTime;
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getSalt() {
return salt;
}
public void setSalt(Integer salt) {
this.salt = salt;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
2.创建mapper接口和mapper.xml
点击查看代码
public interface EvaluationMapper extends BaseMapper<Evaluation> {
}
点击查看代码
public interface MemberMapper extends BaseMapper<Member> {
}
点击查看代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mpper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.imooc.reader.mapper.EvaluationMapper">
</mapper>
<?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.imooc.reader.mapper.BookMapper">
</mapper>
3.接口实现类
点击查看代码
public interface EvaluationService {
public List<Evaluation> selectById(Long bookId);
}
点击查看代码
package com.imooc.reader.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imooc.reader.entity.Book;
import com.imooc.reader.entity.Evaluation;
import com.imooc.reader.entity.Member;
import com.imooc.reader.mapper.BookMapper;
import com.imooc.reader.mapper.EvaluationMapper;
import com.imooc.reader.mapper.MemberMapper;
import com.imooc.reader.service.EvaluationService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service("EvaluationService")
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class EvaluationServiceImp implements EvaluationService {
@Resource
private EvaluationMapper evaluationMapper;
@Resource
private MemberMapper memberMapper;
@Resource
private BookMapper bookMapper;
public List<Evaluation> selectById(Long bookId) {
Book book = bookMapper.selectById(bookId);
QueryWrapper<Evaluation> QueryWrapper = new QueryWrapper<Evaluation>();
QueryWrapper.eq("book_id",bookId);
QueryWrapper.eq("state","enable");
QueryWrapper.orderByDesc("create_time");
List<Evaluation> evaluationList = evaluationMapper.selectList(QueryWrapper);
for (Evaluation eva:evaluationList) {
Long memberId = eva.getMemberId();
Member member = memberMapper.selectById(memberId);
eva.setMember(member);
eva.setBook(book);
}
return evaluationList;
}
}
4.前端
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${book.bookName}</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<link rel="stylesheet" href="/resources/bootstrap/bootstrap.css">
<link rel="stylesheet" href="/resources/raty/lib/jquery.raty.css">
<script src="/resources/jquery.3.3.1.min.js"></script>
<script src="/resources/bootstrap/bootstrap.min.js"></script>
<script src="/resources/art-template.js"></script>
<script src="/resources/raty/lib/jquery.raty.js"></script>
<style>
.container {
padding: 0px;
margin: 0px;
}
.row {
padding: 0px;
margin: 0px;
}
.alert {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.col- * {
padding: 0px;
}
.description p {
text-indent: 2em;
}
.description img {
width: 100%;
}
.highlight {
background-color: lightskyblue !important;
}
</style>
<script>
$.fn.raty.defaults.path = '/resources/raty/lib/images';
$(function () {
$(".stars").raty({readOnly: true});
})
$(function () {
<#if memberReadState??>
$("*[data-read-state='${memberReadState.readState}']").addClass("highlight");
</#if>
<#if !loginMember??>
$("*[data-read-state],#btnEvaluation,*[data-evaluation-id]").click(function () {
$("#exampleModalCenter").modal("show");
})
</#if>
<#if loginMember??>
$("*[data-read-state]").click(function () {
let readState = $(this).data("read-state");
$.post("/update_read_state",{
memberId:${loginMember.memberId},
bookId:${book.bookId},
readState:readState
},function (json) {
if(json.code=="0"){
$("*[data-read-state]").removeClass("highlight");
$("*[data-read-state='" +readState +"']").addClass("highlight");
}
},"json")
})
$("#btnEvaluation").click(function () {
$("#score").raty({})
$("#dlgEvaluation").modal("show")
})
$("#btnSubmit").click(function () {
var score=$("#score").raty("score");
var content=$("#content").val();
if(score==0||$.trim(content)==""){
return;
}
$.post("/evaluate",{
score:score,
bookId:${book.bookId},
memberId:${loginMember.memberId},
content:content
},function (json) {
if(json.code=="0"){
window.location.reload()
}
},"json")
})
$("*[data-evaluation-id]").click(function () {
var evaluationId= $(this).data("evaluation-id");
$.post("/enjoy",{evaluationId:evaluationId},function (json) {
if(json.code=="0"){
$("*[data-evaluation-id='"+evaluationId +"'] span").text(json.evaluate.enjoy);
}
},"json")
})
</#if>
})
</script>
</head>
<body>
<!--<div style="width: 375px;margin-left: auto;margin-right: auto;">-->
<div class="container ">
<nav class="navbar navbar-light bg-white shadow mr-auto">
<ul class="nav">
<li class="nav-item">
<a href="/">
<img src="https://m.imooc.com/static/wap/static/common/img/logo2.png" class="mt-1"
style="width: 100px">
</a>
</li>
</ul>
</nav>
<div class="container mt-2 p-2 m-0" style="background-color:rgb(127, 125, 121)">
<div class="row">
<div class="col-4 mb-2 pl-0 pr-0">
<img style="width: 110px;height: 160px"
src="/images/1.jpg">
</div>
<div class="col-8 pt-2 mb-2 pl-0">
<h6 class="text-white">${book.bookName}</h6>
<div class="p-1 alert alert-warning small" role="alert">
${book.subTitle}
</div>
<p class="mb-1">
<span class="text-white-50 small">${book.author}</span>
</p>
<div class="row pl-1 pr-2">
<div class="col-6 p-1">
<button type="button" data-read-state="1" class="btn btn-light btn-sm w-100">
<img style="width: 1rem;" class="mr-1"
src="https://img3.doubanio.com/f/talion/cf2ab22e9cbc28a2c43de53e39fce7fbc93131d1/pics/card/ic_mark_todo_s.png"/>想看
</button>
</div>
<div class="col-6 p-1">
<button type="button" data-read-state="2" class="btn btn-light btn-sm w-100">
<img style="width: 1rem;" class="mr-1"
src="https://img3.doubanio.com/f/talion/78fc5f5f93ba22451fd7ab36836006cb9cc476ea/pics/card/ic_mark_done_s.png"/>看过
</button>
</div>
</div>
</div>
</div>
<div class="row" style="background-color: rgba(0,0,0,0.1);">
<div class="col-2"><h2 class="text-white">${book.evaluationScore}</h2></div>
<div class="col-5 pt-2">
<span class="stars" data-score="${book.evaluationScore}"></span>
</div>
<div class="col-5 pt-2"><h5 class="text-white">${book.evaluationQuantity}人已评</h5></div>
</div>
</div>
<div class="row p-2 description">
${book.description}
</div>
<div class="alert alert-primary w-100 mt-2" role="alert">短评
<button type="button" id="btnEvaluation" class="btn btn-success btn-sm text-white float-right"
style="margin-top: -3px;">
写短评
</button>
</div>
<div class="reply pl-2 pr-2">
<#list evaluationList as evaluation>
<div>
<div>
<span class="pt-1 small text-black-50 mr-2">${evaluation.createTime?string('MM-dd')}</span>
<span class="mr-2 small pt-1">${evaluation.member.nickname}</span>
<span class="stars mr-2" data-score="${evaluation.score}"></span>
<button type="button" data-evaluation-id="${evaluation.evaluationId}"
class="btn btn-success btn-sm text-white float-right" style="margin-top: -3px;">
<img style="width: 24px;margin-top: -5px;" class="mr-1"
src="https://img3.doubanio.com/f/talion/7a0756b3b6e67b59ea88653bc0cfa14f61ff219d/pics/card/ic_like_gray.svg"/>
<span>${evaluation.enjoy}</span>
</button>
</div>
<div class="row mt-2 small mb-3">
${evaluation.content}
</div>
<hr/>
</div>
</#list>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
您需要登录才可以操作哦~
</div>
<div class="modal-footer">
<a href="/login.html" type="button" class="btn btn-primary">去登录</a>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="dlgEvaluation" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
<h6>${book.bookName}</h6>
<form id="frmEvaluation">
<div class="input-group mt-2 ">
<span id="score"></span>
</div>
<div class="input-group mt-2 ">
<input type="text" id="content" name="content" class="form-control p-4" placeholder="这里输入短评">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" id="btnSubmit" class="btn btn-primary">提交</button>
</div>
</div>
</div>
</div>
</body>
</html>
五.会员注册 Member
1.Kaptcha验证码控制器
点击查看代码
package com.imooc.reader.controller;
import com.google.code.kaptcha.Producer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
@Controller
public class KaptchaController {
@Resource
private Producer kaptchaProducer;
@GetMapping("/verify_code")
private void careatVerifyCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setDateHeader("Expires",0);//响应过期时间
//缓存控制 不存储 不缓存 必须重新校验
response.setHeader("Cache-Control","no-store,no-cache,must-revalidate");
response.setHeader("Pragma","no-cache");
response.setContentType("image/png");
String verifyCode = kaptchaProducer.createText();
request.getSession().setAttribute("kaptchaVerifyCode",verifyCode);
System.out.println( request.getSession().getAttribute("kaptchaVerifyCode"));
BufferedImage image = kaptchaProducer.createImage(verifyCode);
ServletOutputStream outputStream = response.getOutputStream();
ImageIO.write(image,"png",outputStream);
outputStream.flush();
outputStream.close();
}
}
2.前端 ajax提交验证码数据
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>会员注册-慕课书评网</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<link rel="stylesheet" href="./resources/bootstrap/bootstrap.css">
<link rel="stylesheet" href="./resources/raty/lib/jquery.raty.css">
<script src="./resources/jquery.3.3.1.min.js"></script>
<script src="./resources/bootstrap/bootstrap.min.js"></script>
<style>
.container {
padding: 0px;
margin: 0px;
}
.row {
padding: 0px;
margin: 0px;
}
.col- * {
padding: 0px;
}
.description p {
text-indent: 2em;
}
.description img {
width: 100%;
}
</style>
</head>
<body>
<!--<div style="width: 375px;margin-left: auto;margin-right: auto;">-->
<div class="container ">
<nav class="navbar navbar-light bg-white shadow">
<ul class="nav">
<li class="nav-item">
<a href="/">
<img src="https://m.imooc.com/static/wap/static/common/img/logo2.png" class="mt-1"
style="width: 100px">
</a>
</li>
</ul>
</nav>
<div class="container mt-2 p-2 m-0">
<form id="frmLogin">
<div class="passport bg-white">
<h4 class="float-left">会员注册</h4>
<h6 class="float-right pt-2"><a href="/login.html">会员登录</a></h6>
<div class="clearfix"></div>
<div class="alert d-none mt-2" id="tips" role="alert">
</div>
<div class="input-group mt-2 ">
<input type="text" id="username" name="username" class="form-control p-4" placeholder="请输入用户名">
</div>
<div class="input-group mt-4 ">
<input id="password" name="password" class="form-control p-4" placeholder="请输入密码" type="password">
</div>
<div class="input-group mt-4 ">
<input type="text" id="nickname" name="nickname" class="form-control p-4" placeholder="请输入昵称"
>
</div>
<div class="input-group mt-4 ">
<div class="col-5 p-0">
<input type="text" id="verifyCode" name="vc" class="form-control p-4" placeholder="验证码">
</div>
<div class="col-4 p-0 pl-2 pt-0">
<!-- 验证码图片 -->
<img id="imgVerifyCode" src="/verify_code"
style="width: 120px;height:50px;cursor: pointer">
</div>
</div>
<a id="btnSubmit" class="btn btn-success btn-block mt-4 text-white pt-3 pb-3">注 册</a>
</div>
</form>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
您已注册成功
</div>
<div class="modal-footer">
<a href="/login.html" type="button" class="btn btn-primary">去登录</a>
</div>
</div>
</div>
</div>
<script>
//控制错误信息的显示与隐藏
function showTips(isShow, css, text) {
if (isShow) {
$("#tips").removeClass("d-none")
$("#tips").hide();
$("#tips").addClass(css);
$("#tips").text(text);
$("#tips").fadeIn(200);
} else {
$("#tips").text("");
$("#tips").fadeOut(200);
$("#tips").removeClass();
$("#tips").addClass("alert")
}
}
//重新发送请求,刷新验证码
function reloadVerifyCode(){
//请在这里实现刷新验证码
$("#imgVerifyCode").attr("src","/verify_code?ts="+new Date().getTime())
}
//点击验证码图片刷新验证码
$("#imgVerifyCode").click(function () {
reloadVerifyCode();
});
//点击提交按钮,向/registe发起ajax请求
//提交请求包含四个参数
//vc:前台输入验证码 username:用户名 password:密码 nickname:昵称
$("#btnSubmit").click(function () {
//表单校验
var username = $.trim($("#username").val());
var regex = /^.{6,10}$/;
if (!regex.test(username)) {
showTips(true, "alert-danger", "用户名请输入正确格式(6-10位)");
return;
} else {
showTips(false);
}
var password = $.trim($("#password").val());
if (!regex.test(password)) {
showTips(true, "alert-danger", "密码请输入正确格式(6-10位)");
return;
} else {
showTips(false);
}
$btnReg = $(this);
$btnReg.text("正在处理...");
$btnReg.attr("disabled", "disabled");
//发送ajax请求
$.ajax({
url: "/registe",
type: "post",
dataType: "json",
data: $("#frmLogin").serialize(),
success: function (data) {
//结果处理,根据服务器返回code判断服务器处理状态
//服务器要求返回JSON格式:
//{"code":"0","msg":"处理消息"}
console.info("服务器响应:" , data);
if (data.code == "0") {
//显示注册成功对话框
$("#exampleModalCenter").modal({});
$("#exampleModalCenter").modal("show");
} else {
//服务器校验异常,提示错误信息
showTips(true, "alert-danger", data.msg);
reloadVerifyCode();
$btnReg.text("注 册");
$btnReg.removeAttr("disabled");
}
}
});
return false;
});
</script>
</body>
</html>
前面已经创建了Member实体类 mapper
3.创建异常类
点击查看代码
package com.imooc.reader.service.exception;
/**
* BussinessException业务逻辑异常
*/
public class BussinessException extends RuntimeException{
private String code;
private String msg;
public BussinessException(String code , String msg){
super(msg);
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
4.创建MD5Utils
点击查看代码
package com.imooc.reader.utils;
import org.apache.commons.codec.digest.DigestUtils;
public class MD5Utils {
public static String md5Digest(String source , Integer salt){
char[] ca = source.toCharArray();
//混淆源数据
for(int i = 0 ; i < ca.length ; i++){
ca[i] = (char) (ca[i] + salt);
}
String target = new String(ca);
String md5 = DigestUtils.md5Hex(target);
return md5;
}
}
5.新建接口就其实现类
点击查看代码
public interface MemberService {
public Member createMember(String username,String password,String nickname);
}
package com.imooc.reader.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imooc.reader.entity.Evaluation;
import com.imooc.reader.entity.Member;
import com.imooc.reader.entity.MemberReadState;
import com.imooc.reader.mapper.EvaluationMapper;
import com.imooc.reader.mapper.MemberMapper;
import com.imooc.reader.mapper.MemberReadStateMapper;
import com.imooc.reader.service.MemberService;
import com.imooc.reader.service.exception.BussinessException;
import com.imooc.reader.utils.MD5Utils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Random;
@Service("memberService")
@Transactional
public class MemberServiceImp implements MemberService {
@Resource
private MemberMapper memberMapper;
public Member createMember(String username, String password, String nickname) throws BussinessException {
QueryWrapper<Member> queryWrapper = new QueryWrapper<Member>();
queryWrapper.eq("username", username);
List<Member> memberList = memberMapper.selectList(queryWrapper);
//判断用户名是否已存在
if(memberList.size() > 0){
throw new BussinessException("M01","用户名已存在");
}
Member member = new Member();
member.setUsername(username);
member.setNickname(nickname);
int salt = new Random().nextInt(1000) + 1000; //盐值
String md5 = MD5Utils.md5Digest(password, salt);
member.setPassword(md5);
member.setSalt(salt);
member.setCreateTime(new Date());
memberMapper.insert(member);
return member;
}
}
6.验证码和用户注册比对 MemberController
点击查看代码
package com.imooc.reader.controller;
import com.imooc.reader.entity.Evaluation;
import com.imooc.reader.entity.Member;
import com.imooc.reader.service.MemberService;
import com.imooc.reader.service.exception.BussinessException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
@Controller
public class MemberController {
@Resource
private MemberService memberService;
@GetMapping("/register.html")
public ModelAndView showRegister(){
return new ModelAndView("/register");
}
@PostMapping("/registe")
@ResponseBody
public Map registe(String vc, String username, String password , String nickname , HttpServletRequest request){
//正确验证码
String verifyCode = (String)request.getSession().getAttribute("kaptchaVerifyCode");
//验证码对比
Map result = new HashMap();
if(vc == null || verifyCode == null || !vc.equalsIgnoreCase(verifyCode)){
result.put("code", "VC01");
result.put("msg", "验证码错误");
}else{
try {
memberService.createMember(username, password, nickname);
result.put("code", "0");
result.put("msg", "success");
}catch (BussinessException ex){
ex.printStackTrace();
result.put("code", ex.getCode());
result.put("msg", ex.getMsg());
}
}
return result;
}
}
六.会员登录
1.前端 Ajax 提交数据
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>会员登录-慕课书评网</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<link rel="stylesheet" href="http://cdn.itlaoqi.com./resources/bootstrap4/css/bootstrap.css">
<link rel="stylesheet" href="./resources/raty/lib/jquery.raty.css">
<script src="http://cdn.itlaoqi.com./resources/jquery.3.3.1.min.js"></script>
<script src="http://cdn.itlaoqi.com./resources/bootstrap4/js/bootstrap.min.js"></script>
<style>
.container {
padding: 0px;
margin: 0px;
}
.row {
padding: 0px;
margin: 0px;
}
.col- * {
padding: 0px;
}
.description p {
text-indent: 2em;
}
.description img {
width: 100%;
}
</style>
</head>
<body>
<!--<div style="width: 375px;margin-left: auto;margin-right: auto;">-->
<div class="container ">
<nav class="navbar navbar-light bg-white shadow">
<ul class="nav">
<li class="nav-item">
<a href="/">
<img src="https://m.imooc.com/static/wap/static/common/img/logo2.png" class="mt-1"
style="width: 100px">
</a>
</li>
</ul>
</nav>
<div class="container mt-2 p-2 m-0">
<form id="frmLogin">
<div class="passport bg-white">
<h4 class="float-left">会员登录</h4>
<h6 class="float-right pt-2"><a href="/register.html">会员注册</a></h6>
<div class="clearfix"></div>
<div class="alert d-none mt-2" id="tips" role="alert">
</div>
<div class="input-group mt-2 ">
<input type="text" id="username" name="username" class="form-control p-4" placeholder="请输入用户名"
aria-label="Username" aria-describedby="basic-addon1">
</div>
<div class="input-group mt-4 ">
<input id="password" name="password" class="form-control p-4" placeholder="请输入密码" type="password"
aria-describedby="basic-addon1">
</div>
<div class="input-group mt-4 ">
<div class="col-5 p-0">
<input type="text" id="verifyCode" name="vc" class="form-control p-4" placeholder="验证码">
</div>
<div class="col-4 p-0 pl-2 pt-0">
<img id="imgVerifyCode" src="/verify_code"
style="width: 120px;height:50px;cursor: pointer">
</div>
</div>
<a id="btnSubmit" class="btn btn-success btn-block mt-4 text-white pt-3 pb-3">登 录</a>
</div>
</form>
</div>
</div>
<script>
function showTips(isShow, css, text) {
if (isShow) {
$("#tips").removeClass("d-none")
$("#tips").hide();
$("#tips").addClass(css);
$("#tips").text(text);
$("#tips").fadeIn(200);
} else {
$("#tips").text("");
$("#tips").fadeOut(200);
$("#tips").removeClass();
$("#tips").addClass("alert")
}
}
function reloadVerifyCode(){
$("#imgVerifyCode").attr("src", "/verify_code?ts=" + new Date().getTime());
}
$("#imgVerifyCode").click(function () {
reloadVerifyCode();
});
$("#btnSubmit").click(function () {
var username = $.trim($("#username").val());
var regex = /^.{1,10}$/;
if (!regex.test(username)) {
showTips(true, "alert-danger", "用户名请输入正确格式(1-10位)");
return;
} else {
showTips(false);
}
var password = $.trim($("#password").val());
if (!regex.test(password)) {
showTips(true, "alert-danger", "密码请输入正确格式(1-10位)");
return;
} else {
showTips(false);
}
$btnReg = $(this);
$btnReg.text("正在处理...");
$btnReg.attr("disabled", "disabled");
$.ajax({
url: "/check_login",
type: "post",
dataType: "json",
data: $("#frmLogin").serialize(),
success: function (data) {
console.info(data);
if (data.code == "0") {
window.location = "/?ts=" + new Date().getTime();
} else {
showTips(true, "alert-danger", data.msg);
reloadVerifyCode();
$btnReg.text("登录");
$btnReg.removeAttr("disabled");
}
}
});
return false;
});
</script>
</body>
</html>
2.MemberController
点击查看代码
@GetMapping("/login.html")
public ModelAndView showLogin(){
return new ModelAndView("/login");
}
3.service接口和实现类
点击查看代码
public Member checkLogin(String username,String password);
public Member checkLogin(String username, String password) {
QueryWrapper<Member> memberQueryWrapper = new QueryWrapper<Member>();
memberQueryWrapper.eq("username",username);
Member member = memberMapper.selectOne(memberQueryWrapper);
if(member==null){
throw new BussinessException("M02","用户不存在");
}else{
String md5 = MD5Utils.md5Digest(password, member.getSalt());
if( !md5.equals(member.getPassword())){
throw new BussinessException("M03","输入密码有误");
}
}
return member;
}
4.控制器MemberController 校验
点击查看代码
@PostMapping("/check_login")
@ResponseBody
public Map checkLogin(String username, String password, String vc, HttpSession session){
//正确验证码
String verifyCode = (String)session.getAttribute("kaptchaVerifyCode");
//验证码对比
Map result = new HashMap();
if(vc == null || verifyCode == null || !vc.equalsIgnoreCase(verifyCode)){
result.put("code", "VC01");
result.put("msg", "验证码错误");
}else{
try{
Member member = memberService.checkLogin(username, password);
session.setAttribute("loginMember",member);
result.put("code", "0");
result.put("msg", "success");
}catch (BussinessException ex){
ex.printStackTrace();
result.put("code", ex.getCode());
result.put("msg", ex.getMsg());
}
}
return result;
}
七.会员阅读状态显示
1.实体类MemberReadState表
点击查看代码
package com.imooc.reader.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
@TableName("member_read_state")
public class MemberReadState {
@TableId(type = IdType.AUTO)
private Long rsId;
private Long bookId;
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
private Long memberId;
private Integer readState;
private Date createTime;
public Long getRsId() {
return rsId;
}
public void setRsId(Long rsId) {
this.rsId = rsId;
}
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
public Integer getReadState() {
return readState;
}
public void setReadState(Integer readState) {
this.readState = readState;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
2.mapper接口 mapper.xml
public interface MemberReadStateMapper extends BaseMapper<MemberReadState> {
}
<?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.imooc.reader.mapper.MemberReadStateMapper">
</mapper>
3.在Memberservice中创建方法
获取当前会员的阅读状态
点击查看代码
public MemberReadState selectMemberReadState(Long memberId,Long bookId);
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public MemberReadState selectMemberReadState(Long memberId, Long bookId) {
QueryWrapper<MemberReadState> memberQueryWrapper = new QueryWrapper<MemberReadState>();
memberQueryWrapper.eq("book_id",bookId);
memberQueryWrapper.eq("member_id",memberId);
MemberReadState memberReadState = memberReadStateMapper.selectOne(memberQueryWrapper);
return memberReadState;
}
4.在加载图书详情时调用阅读状态
点击查看代码
@GetMapping("/book/{id}")
public ModelAndView showDetail(@PathVariable("id") Long id, HttpSession session){
Book book = bookService.selectById(id);
List<Evaluation> evaluationList = evaluationService.selectById(id);
ModelAndView modelAndView = new ModelAndView("/detail");
Member member=(Member)session.getAttribute("loginMember");
if(member!=null){
MemberReadState memberReadState = memberService.selectMemberReadState(member.getMemberId(), id);
modelAndView.addObject("memberReadState",memberReadState);
}
modelAndView.addObject("book",book);
modelAndView.addObject("evaluationList",evaluationList);
return modelAndView;
}
5.前端
点击查看代码
$(function () {
<#if memberReadState??>
$("*[data-read-state='${memberReadState.readState}']").addClass("highlight");
</#if>
<#if !loginMember??>
$("*[data-read-state],#btnEvaluation,*[data-evaluation-id]").click(function () {
$("#exampleModalCenter").modal("show");
})
</#if>
八.更新会员阅读状态
1.前端
点击查看代码
$(function () {
<#if memberReadState??>
$("*[data-read-state='${memberReadState.readState}']").addClass("highlight");
</#if>
//未登录情况下 显示需要登录
<#if !loginMember??>
$("*[data-read-state],#btnEvaluation,*[data-evaluation-id]").click(function () {
$("#exampleModalCenter").modal("show");
})
</#if>
<#if loginMember??>
$("*[data-read-state]").click(function () {
let readState = $(this).data("read-state");
$.post("/update_read_state",{
memberId:${loginMember.memberId},
bookId:${book.bookId},
readState:readState
},function (json) {
if(json.code=="0"){
$("*[data-read-state]").removeClass("highlight");
$("*[data-read-state='" +readState +"']").addClass("highlight");
}
},"json")
})
$("#btnEvaluation").click(function () {
$("#score").raty({})
$("#dlgEvaluation").modal("show")
})
$("#btnSubmit").click(function () {
var score=$("#score").raty("score");
var content=$("#content").val();
if(score==0||$.trim(content)==""){
return;
}
$.post("/evaluate",{
score:score,
bookId:${book.bookId},
memberId:${loginMember.memberId},
content:content
},function (json) {
if(json.code=="0"){
window.location.reload()
}
},"json")
})
$("*[data-evaluation-id]").click(function () {
var evaluationId= $(this).data("evaluation-id");
$.post("/enjoy",{evaluationId:evaluationId},function (json) {
if(json.code=="0"){
$("*[data-evaluation-id='"+evaluationId +"'] span").text(json.evaluate.enjoy);
}
},"json")
})
</#if>
})
2.在MemberService添加更新阅读状态方法
点击查看代码
public MemberReadState updateMemberReadState(Long memberId,Long bookId,Integer readState);
public MemberReadState updateMemberReadState(Long memberId, Long bookId, Integer readState) {
QueryWrapper<MemberReadState> memberReadStateQueryWrapper = new QueryWrapper<MemberReadState>();
memberReadStateQueryWrapper.eq("book_id",bookId);
memberReadStateQueryWrapper.eq("member_id",memberId);
MemberReadState memberReadState = memberReadStateMapper.selectOne(memberReadStateQueryWrapper);
if(memberReadState==null){
memberReadState=new MemberReadState();
memberReadState.setMemberId(memberId);
memberReadState.setBookId(bookId);
memberReadState.setReadState(readState);
memberReadState.setCreateTime(new Date());
memberReadStateMapper.insert(memberReadState);
}else {
memberReadState.setReadState(readState);
memberReadStateMapper.updateById(memberReadState);
}
return memberReadState;
}
3.MemberController调用
点击查看代码
@PostMapping("/update_read_state")
@ResponseBody
public Map updateReadState(Long memberId,Long bookId,Integer readState){
Map map=new HashMap();
try {
memberService.updateMemberReadState(memberId, bookId, readState);
map.put("code","0");
map.put("msg","success");
}catch (BussinessException ex){
map.put("caode",ex.getCode());
map.put("msg",ex.getMsg());
}
return map;
}
九.写短评
1.在 <#if loginMember??>在建立点击事件,显示评分框
点击查看代码
$("#btnEvaluation").click(function () {
$("#score").raty({})
$("#dlgEvaluation").modal("show")
})
在写短评填充书名
2. 在Memberservice中添加方法
点击查看代码
public Evaluation evaluate(Long memberId, Long bookId, Integer score, String content);
public Evaluation evaluate(Long memberId, Long bookId, Integer score, String content) {
Evaluation evaluation=new Evaluation();
evaluation.setMemberId(memberId);
evaluation.setBookId(bookId);
evaluation.setScore(score);
evaluation.setContent(content);
evaluation.setCreateTime(new Date());
evaluation.setState("enable");
evaluation.setEnjoy(0);
evaluationMapper.insert(evaluation);
return evaluation;
}
3.控制器MemberController
点击查看代码
@PostMapping("/evaluation")
public Map evaluation(Long memberId, Long bookId, Integer score, String content){
Map map=new HashMap();
try {
Evaluation evaluate = memberService.evaluate(memberId, bookId, score, content);
map.put("code","0");
map.put("msg","success");
map.put("evaluate",evaluate);
}catch (BussinessException ex){
map.put("caode",ex.getCode());
map.put("msg",ex.getMsg());
}
return map;
}
4.前端Ajax提交
$("#btnEvaluation").click(function () {
$("#score").raty({})
$("#dlgEvaluation").modal("show")
})
$("#btnSubmit").click(function () {
var score=$("#score").raty("score");
var content=$("#content").val();
if(score==0||$.trim(content)==""){
return;
}
$.post("/evaluate",{
score:score,
bookId:${book.bookId},
memberId:${loginMember.memberId},
content:content
},function (json) {
if(json.code=="0"){
window.location.reload()
}
},"json")
})
十.会员点赞
1.在MemberService创建方法
点击查看代码
public Evaluation enjoy(Long evaluationId);
public Evaluation enjoy(Long evaluationId) {
Evaluation evaluation = evaluationMapper.selectById(evaluationId);
evaluation.setEnjoy(evaluation.getEnjoy()+1);
evaluationMapper.updateById(evaluation);
return evaluation;
}
2.在MemberController调用
点击查看代码
@PostMapping("/enjoy")
@ResponseBody
public Map enjoy(Long evaluationId){
Map map=new HashMap();
try {
Evaluation evaluate = memberService.enjoy(evaluationId);
map.put("code","0");
map.put("msg","success");
map.put("evaluate",evaluate);
}catch (BussinessException ex){
map.put("code",ex.getCode());
map.put("msg",ex.getMsg());
}
return map;
}
3.前端
点击查看代码
$("*[data-evaluation-id]").click(function () {
var evaluationId= $(this).data("evaluation-id");
$.post("/enjoy",{evaluationId:evaluationId},function (json) {
if(json.code=="0"){
$("*[data-evaluation-id='"+evaluationId +"'] span").text(json.evaluate.enjoy);
}
},"json")
})
十一.评分自动计算
spring-task
1.在bookMapper.xml中添加
点击查看代码
<update id="updateEvaluation">
update book b SET evaluation_score = (
select ifnull(avg(score),0) from evaluation where book_id = b.book_id and state='enable'
),evaluation_quantity = (
select ifnull(count(*),0) from evaluation where book_id = b.book_id and state='enable'
)
</update>
2.在BookMapper定义
点击查看代码
public interface BookMapper extends BaseMapper<Book> {
public void updateEvaluation();
}
3.bookService中实现方法
点击查看代码
@Transactional
public void updateEvaluation() {
bookMapper.updateEvaluation();
}
4.引入依赖 配置开启注解
点击查看代码
<task:annotation-driven/>
5.创建任务组件
点击查看代码
package com.imooc.reader.task;
import com.imooc.reader.service.BookService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class ComputerTask {
@Resource
private BookService bookService;
//调度
@Scheduled(cron = "0 * * * * ?")
public void updateEvaluation(){
bookService.updateEvaluation();
System.out.println("已更新所以评分");
}
}
十二:wangedit图片上传
1.创建MbookController
点击查看代码
package com.imooc.reader.controller.management;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.imooc.reader.entity.Book;
import com.imooc.reader.service.BookService;
import com.imooc.reader.service.exception.BussinessException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/management/book")
public class MBookController {
@GetMapping("/index.html")
public ModelAndView showBook(){
return new ModelAndView("/management/book");
}
}
2.前端 初始化wangEditor
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图书管理</title>
<style>
#dlgBook{
padding: 10px
}
</style>
<link rel="stylesheet" href="/resources/layui/css/layui.css">
<script src="/resources/wangEditor.min.js"></script>
<script type="text/html" id="toolbar">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" id="btnAdd" onclick="showCreate()">添加</button>
</div>
</script>
</head>
<body>
<div class="layui-container">
<blockquote class="layui-elem-quote">图书列表</blockquote>
<!-- 数据表格 -->
<table id="grdBook" lay-filter="grdBook"></table>
</div>
<!--表单内容-->
<div id="dialog" style="padding: 10px;display: none">
<form class="layui-form" >
<div class="layui-form-item">
<!-- 图书类别 -->
<select id="categoryId" name="categoryId" lay-verify="required" lay-filter=
"categoryId">
<option value=""></option>
<option value="1">前端</option>
<option value="2">后端</option>
<option value="3">测试</option>
<option value="4">产品</option>
</select>
</div>
<div class="layui-form-item">
<!-- 书名 -->
<input type="text" id="bookName" name="bookName" required lay-verify="required" placeholder="请输入书名"
autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<!-- 子标题 -->
<input type="text" id="subTitle" name="subTitle" required lay-verify="required" placeholder="请输入子标题"
autocomplete="off" class="layui-input">
</div>
<div class="layui-form-item">
<!-- 作者 -->
<input type="text" id="author" name="author" required lay-verify="required" placeholder="请输入作者信息"
autocomplete="off" class="layui-input">
</div>
<div style="margin-top: 30px;font-size: 130%">图书介绍(默认第一图将作为图书封面)</div>
<div class="layui-form-item" >
<!-- wangEditor编辑器 -->
<div id="editor" style="width: 100%">
</div>
</div>
<!-- 图书编号 -->
<input id="bookId" type="hidden">
<!-- 当前表单操作类型,create代表新增 update代表修改 -->
<input id="optype" type="hidden">
<div class="layui-form-item" style="text-align: center">
<!-- 提交按钮 -->
<button class="layui-btn" lay-submit="" lay-filter="btnSubmit">立即提交</button>
</div>
</form>
</div>
<script src="/resources/layui/layui.all.js"></script>
<script>
var table = layui.table; //table数据表格对象
var $ = layui.$; //jQuery
var editor = null; //wangEditor富文本编辑器对象
//初始化图书列表
table.render({
elem: '#grdBook' //指定div
, id : "bookList" //数据表格id
, toolbar: "#toolbar" //指定工具栏,包含新增添加
, url: "/management/book/list" //数据接口
, page: true //开启分页
, cols: [[ //表头
{field: 'bookName', title: '书名', width: '300'}
, {field: 'subTitle', title: '子标题', width: '200'}
, {field: 'author', title: '作者', width: '200'}
, {type: 'space', title: '操作', width: '200' , templet : function(d){
//为每一行表格数据生成"修改"与"删除"按钮,并附加data-id属性代表图书编号
return "<button class='layui-btn layui-btn-sm btn-update' data-id='" + d.bookId + "' data-type='update' onclick='showUpdate(this)'>修改</button>" +
"<button class='layui-btn layui-btn-sm btn-delete' data-id='" + d.bookId + "' onclick='showDelete(this)'>删除</button>";
}
}
]]
});
//显示更新图书对话框
//obj对应点击的"修改"按钮对象
function showUpdate(obj){
//弹出"编辑图书"对话框
layui.layer.open({
id: "dlgBook", //指定div
title: "编辑图书", //标题
type: 1,
content: $('#dialog').html(), //设置对话框内容,复制自dialog DIV
area: ['820px', '730px'], //设置对话框宽度高度
resize: false //是否允许调整尺寸
})
var bookId = $(obj).data("id"); //获取"修改"按钮附带的图书编号
$("#dlgBook #bookId").val(bookId); //为表单隐藏域赋值,提交表单时用到
editor = new wangEditor('#dlgBook #editor'); //初始化富文本编辑器
editor.customConfig.uploadImgServer = '/management/book/upload' //设置图片上传路径
editor.customConfig.uploadFileName = 'img'; //图片上传时的参数名
editor.create(); //创建wangEditor
$("#dlgBook #optype").val("update"); //设置当前表单提交时提交至"update"更新地址
//发送ajax请求,获取对应图书信息
$.get("/management/book/id/" + bookId , {} , function(json){
//文本框回填已有数据
$("#dlgBook #bookName").val(json.data.bookName);//书名
$("#dlgBook #subTitle").val(json.data.subTitle); //子标题
$("#dlgBook #author").val(json.data.author);//作者
$("#dlgBook #categoryId").val(json.data.categoryId); //分类选项
editor.txt.html(json.data.description); //设置图文内容
layui.form.render();//重新渲染LayUI表单
} , "json")
}
//显示新增图书对话框
function showCreate(){
//弹出"新增图书"对话框
layui.layer.open({
id: "dlgBook",
title: "新增图书",
type: 1,
content: $('#dialog').html(),
area: ['820px', '730px'],
resize: false
})
//初始化wangEditor
editor = new wangEditor('#dlgBook #editor');
editor.customConfig.uploadImgServer = '/management/book/upload';//设置图片上传地址
editor.customConfig.uploadFileName = 'img';//设置图片上传参数
editor.create();//创建wangEditor
layui.form.render(); //LayUI表单重新
$("#dlgBook #optype").val("create");//设置当前表单提交时提交至"create"新增地址
};
//对话框表单提交
layui.form.on('submit(btnSubmit)', function(data){
//获取表单数据
var formData = data.field;
//判断是否包含至少一副图片,默认第一图作为封面显示
var description = editor.txt.html();
if(description.indexOf("img") == -1){
layui.layer.msg('请放置一副图片作为封面');
return false;
}
//获取当前表单要提交的地址
//如果是新增数据则提交至create
//如果是更新数据则提交至update
var optype = $("#dlgBook #optype").val();
if(optype == "update"){
//更新数据时,提交时需要附加图书编号
formData.bookId=$("#dlgBook #bookId").val();
}
//附加图书详细描述的图文html
formData.description = description;
//向服务器发送请求
$.post("/management/book/" + optype , formData , function(json){
if(json.code=="0"){
//处理成功,关闭对话框,刷新列表,提示操作成功
layui.layer.closeAll();
table.reload('bookList');
layui.layer.msg('数据操作成功,图书列表已刷新');
}else{
//处理失败,提示错误信息
layui.layer.msg(json.msg);
}
} ,"json")
return false;
});
//删除图书
function showDelete(obj){
//获取当前点击的删除按钮中包含的图书编号
var bookId = $(obj).data("id");
//利用layui的询问对话框进行确认
layui.layer.confirm('确定要执行删除操作吗?', {icon: 3, title:'提示'}, function(index){
//确认按钮后发送ajax请求,包含图书编号
$.get("/management/book/delete/" + bookId, {}, function (json) {
if(json.code=="0"){
//删除成功刷新表格
table.reload('bookList');
//提示操作成功
layui.layer.msg('数据操作成功,图书列表已刷新');
//关闭对话框
layui.layer.close(index);
}else{
//处理失败,提示错误信息
layui.layer.msg(json.msg);
}
}, "json");
});
}
</script>
</body>
</html>
3.导入文件上传依赖和配置
点击查看代码
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
</bean>
4.在MbookController新增方法
点击查看代码
@PostMapping("/upload")
@ResponseBody
public Map upload(@RequestParam("img") MultipartFile file, HttpServletRequest request) throws IOException {//img是前端界面设置的文件名
String uploadPath=request.getServletContext().getResource("/").getPath()+"/upload/";//运行时根路径
String fileName=new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
file.transferTo(new File(uploadPath+fileName+suffix));//另存为
Map result=new HashMap();
result.put("errno",0);
result.put("data",new String[]{"/upload/"+fileName+suffix});
return result;
}
十三:图书新增
1.在bookService添加方法
点击查看代码
public Book createBook(Book book);
@Transactional
public Book createBook(Book book) {
bookMapper.insert(book);
return book;//自动回填 有id值
}
2.在MBookController新增方法
Jsoup.parse(html代码).select(语法类似JQuery)
点击查看代码
@PostMapping("/create")
@ResponseBody
private Map createBook(Book book){
Map result=new HashMap();
try{
book.setEvaluationQuantity(0);
book.setEvaluationScore(0f);
Document document = Jsoup.parse(book.getDescription());
Element img = document.select("img").first();
String cover = img.attr("src");
book.setCover(cover);
bookService.createBook(book);
result.put("code",0);
result.put("msg","success");
}catch(BussinessException ex){
result.put("code",ex.getCode());
result.put("msg",ex.getMsg());
}
return result;
}
十三.图书分页查询
1.在MBookController新增方法
点击查看代码
@GetMapping("/list")
@ResponseBody
public Map list(Integer page,Integer limit){
if(page==null){
page=1;
}
if(page==limit){
limit=10;
}
IPage<Book> bookIPage = bookService.selectBookPage(null, null, page, limit);
Map result=new HashMap();
result.put("code",0);
result.put("msg","success");
result.put("data",bookIPage.getRecords());
result.put("count",bookIPage.getTotal());
return result;
}
十四:图书修改更新
1.前端
点击查看代码
table.render({
elem: '#grdBook' //指定div
, id : "bookList" //数据表格id
, toolbar: "#toolbar" //指定工具栏,包含新增添加
, url: "/management/book/list" //数据接口
, page: true //开启分页
, cols: [[ //表头
{field: 'bookName', title: '书名', width: '300'}
, {field: 'subTitle', title: '子标题', width: '200'}
, {field: 'author', title: '作者', width: '200'}
, {type: 'space', title: '操作', width: '200' , templet : function(d){
//为每一行表格数据生成"修改"与"删除"按钮,并附加data-id属性代表图书编号
return "<button class='layui-btn layui-btn-sm btn-update' data-id='" + d.bookId + "' data-type='update' onclick='showUpdate(this)'>修改</button>" +
"<button class='layui-btn layui-btn-sm btn-delete' data-id='" + d.bookId + "' onclick='showDelete(this)'>删除</button>";
}
}
]]
});
2.在bookService定义
点击查看代码
public Book updateBook(Book book);
@Transactional
public Book updateBook(Book book) {
bookMapper.updateById(book);
return book;
3.数据回显 在mbookController添加方法
点击查看代码
@GetMapping("/id/{id}")
@ResponseBody
public Map selectById(@PathVariable("id") Long bookId){
Book book = bookService.selectById(bookId);
Map result=new HashMap();
result.put("code",0);
result.put("msg","success");
result.put("data",book);
return result;
}
4.数据更新 在mbookController添加方法,根据bookid查询到原有的数据,在此基础上更新
点击查看代码
@PostMapping("/update")
@ResponseBody
public Map updateBook(Book book){
Map result = new HashMap();
try {
Book rawBook = bookService.selectById(book.getBookId());
rawBook.setBookName(book.getBookName());
rawBook.setSubTitle(book.getSubTitle());
rawBook.setCategoryId(book.getCategoryId());
rawBook.setDescription(book.getDescription());
Document doc = Jsoup.parse(book.getDescription());
String cover = doc.select("img").first().attr("src");
rawBook.setCover(cover);
bookService.updateBook(rawBook);
result.put("code", 0);
result.put("msg", "success");
}catch (BussinessException ex){
ex.printStackTrace();
result.put("code", ex.getCode());
result.put("msg",ex.getMsg());
}
return result;
}
十五:图书删除
1.在bookService定义 需要删除阅读状态和评论数据
点击查看代码
public void deleteBook(Long bookId);
@Transactional
public void deleteBook(Long bookId) {
bookMapper.deleteById(bookId);
QueryWrapper<MemberReadState> memberReadStateQueryWrapper = new QueryWrapper<MemberReadState>();
memberReadStateQueryWrapper.eq("book_id",bookId);
memberReadStateMapper.delete(memberReadStateQueryWrapper);
QueryWrapper<Evaluation> evaluationQueryWrapper = new QueryWrapper<Evaluation>();
evaluationQueryWrapper.eq("book_id",bookId);
evaluationMapper.delete(evaluationQueryWrapper);
}
2.在mbookController添加方法
点击查看代码
@GetMapping("/delete/{id}")
@ResponseBody
public Map deleteBook(@PathVariable("id") Long bookId){
Map result = new HashMap();
try {
bookService.deleteBook(bookId);
result.put("code", 0);
result.put("msg", "success");
}catch (BussinessException ex){
ex.printStackTrace();
result.put("code", ex.getCode());
result.put("msg",ex.getMsg());
}
return result;
}
十六.后台首页
点击查看代码
@Controller
@RequestMapping("/management")
public class ManagementController {
@GetMapping("/index.html")
public ModelAndView showIndex(){
return new ModelAndView("/management/index");
}
}

浙公网安备 33010602011771号