小众网
1.使用到的技术有 MyBatis-Plus,Spring-mvc, freemarker
项目的结构图如下:
web.xm文件
<?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>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--中文乱码-->
<filter>
<filter-name>filter</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>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
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/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
</bean>
<!-- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="typeAliasesPackage" value="com.pro.domain"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.pro.mapper"/>
</bean>
<!--扫描-->
<context:component-scan base-package="com.pro"/>
<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>
<!--对于静态资源的访问使用tomcat中的配置-->
<mvc:default-servlet-handler/>
<!--配置fmk的模板引擎-->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl"/>
<property name="freemarkerSettings">
<props>
<!--UTF-8-->
<prop key="defaultEncoding">UTF-8</prop>
</props>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<!--视图解析器,将试图和模板结合在一起 差生新的html片段 -->
<!--设置相应编码-->
<property name="contentType" value="text/html;charset=utf-8"/>
<property name="suffix" value=".ftl"/>
</bean>
<!-- 声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--配置 kaptcha-->
<bean id="defaultKaptcha" 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><!--宽度120px-->
<prop key="kaptcha.textproducer.font.color">blue</prop><!--color-->
<prop key="kaptcha.textproducer.font.size">40</prop><!--字大小-->
<prop key="kaptcha.textproducer.char.length">4</prop><!--几个字符-->
</props>
</constructor-arg>
</bean>
</property>
</bean>
<!-- <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/html/"/>
<property name="suffix" value=".html"/>
</bean>
-->
</beans>
mybatis-config.xml 要引入苞米豆的设置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/>
</plugins>
</configuration>
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/meituan
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=5
jdbc.maxActive=20
pom.xml
<?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>
<parent>
<groupId>web2</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<packaging>war</packaging>
<artifactId>xzw</artifactId>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--加入依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.18</version>
</dependency>
<!--模版语言-->
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!--spring 支持fk-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!--加密解密组件-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
</project>
数据库表:
/*
Navicat Premium Data Transfer
Source Server : xy
Source Server Type : MySQL
Source Server Version : 80032
Source Host : localhost:3306
Source Schema : meituan
Target Server Type : MySQL
Target Server Version : 80032
File Encoding : 65001
Date: 21/10/2023 09:30:29
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for book
-- ----------------------------
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`book_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`book_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '书名',
`sub_title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '子标题',
`author` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '作者',
`cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '封面图URL',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图书描述',
`category_id` bigint NOT NULL COMMENT '类别',
`evaluation_score` float(255, 1) NOT NULL DEFAULT 0.0 COMMENT '图书评分',
`evaluation_quantity` int NOT NULL DEFAULT 0 COMMENT '评价量',
PRIMARY KEY (`book_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of book
-- ----------------------------
INSERT INTO `book` VALUES (1, '可可私房菜', '私房菜', '可可', 'ccsfc.jpg', '好吃不贵买一送一', 3, 4.0, 110);
INSERT INTO `book` VALUES (2, '经典美味家常菜', '家常菜', '今年', 'jdmwjcc.jpg', '经典', 3, 3.0, 60);
INSERT INTO `book` VALUES (3, '萝卜白菜', '白菜', '萝卜', 'lbbc.jpg', '清香淡雅', 3, 1.0, 20);
INSERT INTO `book` VALUES (4, '肉菜真好吃', '美味', '少华', 'rchhc.jpg', '肉食爱好者的福音,牛羊猪、鸡鸭鱼,一网打尽,每道菜都足够让你大显身手。有连厨房新手也能轻松搞定的,吃起来很过瘾的“酸汤肥牛”;有改良后不油不腻,好吃到停不下来的“春笋五花肉”;有挑食的朋友也吃不够的“黑椒排骨”;有端上桌就能立马吸引你筷子去向的“鲍鱼烧鸡”“蒜蓉胡椒虾球”;还有吃完意犹未尽,还得舔舔手指头的“香辣花螺”。你可一定要试试看啊。', 3, 4.9, 666);
INSERT INTO `book` VALUES (5, '水果切雕', '水果', '水果', 'sgqdbp.jpg', '解渴', 3, 4.5, 333);
INSERT INTO `book` VALUES (6, '烧烤宝典', '烧烤', '烧烤', 'skbd.jpg', '淄博烧烤', 3, 4.5, 345);
INSERT INTO `book` VALUES (7, '蔬菜精做', '蔬菜', '蔬菜', 'swzzz.jpg', '蔬菜', 3, 0.0, 0);
INSERT INTO `book` VALUES (8, 'E xcel', '工作助手', '工作助手', '1.jpg', '工作助手', 4, 0.0, 0);
-- ----------------------------
-- Table structure for category
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
`category_id` bigint NOT NULL AUTO_INCREMENT COMMENT '类编号',
`category_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '书分类',
PRIMARY KEY (`category_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of category
-- ----------------------------
INSERT INTO `category` VALUES (1, '文学');
INSERT INTO `category` VALUES (2, '历史');
INSERT INTO `category` VALUES (3, '美食');
INSERT INTO `category` VALUES (4, '艺术');
-- ----------------------------
-- Table structure for evaluation
-- ----------------------------
DROP TABLE IF EXISTS `evaluation`;
CREATE TABLE `evaluation` (
`evaluation_id` bigint NOT NULL AUTO_INCREMENT COMMENT '评价编号',
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '短评内容',
`score` int NOT NULL COMMENT '评分-5分制',
`create_time` datetime NOT NULL COMMENT '创建时间',
`member_id` bigint NOT NULL COMMENT '会员编号',
`book_id` bigint NOT NULL COMMENT '图书编号',
`enjoy` int NOT NULL DEFAULT 0 COMMENT '点赞数量',
`state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'enable' COMMENT '审核状态 enable-有效 disable-已禁用',
`disable_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '禁用理由',
`disable_time` datetime NULL DEFAULT NULL COMMENT '禁用时间',
PRIMARY KEY (`evaluation_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of evaluation
-- ----------------------------
INSERT INTO `evaluation` VALUES (1, ' 只是心口有点堵,看不到真爱的结尾,看不到幸福圆满的结局,主人公孙少平毅然的走着自己的坚实,不卑不亢,不屈不饶,那些真心的爱没有成为他的幸福,或遗憾,或退避,都是为了爱。一种大义凛然,包容博爱,真正男人的形象。恐怕这就是作者所期望读者所领悟的,一种奋发的精神。\r\n', 5, '2023-10-19 15:57:33', 1, 4, 111, 'enable', '无', '2023-10-19 15:58:18');
INSERT INTO `evaluation` VALUES (2, '肉食爱好者的福音,牛羊猪、鸡鸭鱼,一网打尽,每道菜都足够让你大显身手。有连厨房新手也能轻松搞定的,吃起来很过瘾的“酸汤肥牛”;有改良后不油不腻,好吃到停不下来的“春笋五花肉”;有挑食的朋友也吃不够的“黑椒排骨”;有端上桌就能立马吸引你筷子去向的“鲍鱼烧鸡”“蒜蓉胡椒虾球”;还有吃完意犹未尽,还得舔舔手指头的“香辣花螺”。你可一定要试试看啊。', 4, '2023-10-19 16:10:01', 2, 4, 50, 'enable', '无', '2023-10-19 16:10:21');
-- ----------------------------
-- Table structure for member
-- ----------------------------
DROP TABLE IF EXISTS `member`;
CREATE TABLE `member` (
`member_id` bigint NOT NULL AUTO_INCREMENT COMMENT '会员编号',
`username` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
`salt` int NOT NULL COMMENT '盐值',
`create_time` datetime NOT NULL COMMENT '创建时间',
`nickname` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '昵称',
PRIMARY KEY (`member_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of member
-- ----------------------------
INSERT INTO `member` VALUES (1, 'wzmwzm', '74bc40cf4623aa99c07e7169302acf74', 1713, '2023-10-20 14:20:45', 'wzm');
INSERT INTO `member` VALUES (2, 'wzmwzm1', '47986f935974461014db9d9ac7619374', 1502, '2023-10-20 14:22:35', 'wzm');
INSERT INTO `member` VALUES (6, '123456', '5fe5733e150216839b9f876ba946e097', 1851, '2023-10-20 15:33:16', '123456');
-- ----------------------------
-- Table structure for member_read_state
-- ----------------------------
DROP TABLE IF EXISTS `member_read_state`;
CREATE TABLE `member_read_state` (
`rs_id` bigint NOT NULL AUTO_INCREMENT COMMENT '会员阅读状态',
`book_id` bigint NOT NULL COMMENT '图书编号',
`member_id` bigint NOT NULL COMMENT '会员编号',
`read_state` int NOT NULL COMMENT '阅读状态 1-想看 2-看过',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`rs_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of member_read_state
-- ----------------------------
-- ----------------------------
-- Table structure for test
-- ----------------------------
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
`id` int NOT NULL AUTO_INCREMENT,
`content` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of test
-- ----------------------------
INSERT INTO `test` VALUES (1, '测试');
INSERT INTO `test` VALUES (2, '测试');
INSERT INTO `test` VALUES (3, '另一种测试');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户编号',
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
`salt` int NOT NULL COMMENT '盐值',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
先从MyBatis-Plus 说起
要先建立一个实体类的Mapper 接口类 让这个接口继承BaseMapper<实体类> 然后我们就能用他封装好的方法了,我们就在Service层中注入这个Mapper ,要使用 @Autowired 注解 下面我就详细的说说我的 Service 层
这个是BookService的接口
public interface BookService {
//分页
public IPage<Book> paging(Long categoryId,String order, Integer page,Integer rows);
//根据书号查书
public Book getBookById(Long bookId);
}
这个是BookService的接口的实现类 在这个类上我写了一个注解@Service 就是为了能让视图扫描器扫描到这个注解
@Autowired 是将BookMapper 注入到这个实现类中
package com.pro.service;
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pro.domain.Book;
import com.pro.service.BookService;
import com.pro.mapper.BookMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author xy
* @description 针对表【book】的数据库操作Service实现
* @createDate 2023-10-18 15:00:08
*/
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookMapper;
//用好封装好的方法来分页
@Override
public IPage<Book> paging(Long categoryId,String order,Integer page, Integer rows) {
Page<Book> p = new Page<>(page, rows);
QueryWrapper<Book> wrapper = new QueryWrapper<>();
//类别
if(categoryId!=null&&categoryId!=-1){
wrapper.eq("category_id",categoryId);
}
//热度或者评分
if (order!=null){
if ("quantity".equals(order)){
wrapper.orderByDesc("evaluation_quantity");
} else if ("score".equals(order)){
wrapper.orderByDesc("evaluation_score");
}
}
IPage<Book> pageObject = bookMapper.selectPage(p, wrapper);
return pageObject;
}
@Override
public Book getBookById(Long bookId) {
Book book = bookMapper.selectById(bookId);
return book;
}
}
下面就是 BookController 顾名思义Controller 所以要在类上加上一个@Controller注解
package com.pro.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.pro.domain.Book;
import com.pro.domain.Category;
import com.pro.domain.Evaluation;
import com.pro.service.BookService;
import com.pro.service.CategoryService;
import com.pro.service.EvaluationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
public class BookController {
@Autowired
private CategoryService categoryService;
@Autowired
private BookService bookService;
@Autowired
private EvaluationService evaluationService;
@GetMapping("/")
public ModelAndView showIndex() {
ModelAndView modelAndView = new ModelAndView("/index");
List<Category> categories = categoryService.selectAll();
modelAndView.addObject("list", categories);
return modelAndView;
}
@GetMapping("/books")
@ResponseBody
public IPage<Book> selectBooks(Long categoryId, String order, Integer page) {
if (page == null) {
page = 1;
}
IPage<Book> pageObj = bookService.paging(categoryId, order, page, 2);
return pageObj;
}
//查单个书
@GetMapping("/book/{bookId}")
public ModelAndView displayBookDetail(@PathVariable("bookId") Long bookId) {
//得到了书的详细信息
Book book = bookService.getBookById(bookId);
//得到评论列表
List<Evaluation> evlist = evaluationService.getEvaluationByBookId(bookId);
ModelAndView mav = new ModelAndView("/detail");
mav.addObject("book", book);
mav.addObject("evList",evlist);
return mav;
}
}
这个是会员登录和注册 Service
1.注册就是根据传入的username 查寻数据库中是否有这个用户,有的话就直接抛出异常 验证码操作是在控制层进行的 ,颜值的话就是随机生成一个随机数加到传入的密码中然后再进行 MD5加密 是这个密码难以被破解。
2.登录根注册的道理差不多,通过查询是否有这个用户名,把这个用户查出来后,在进行密码的比对。
package com.pro.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pro.domain.Member;
import com.pro.mapper.MemberMapper;
import com.pro.service.exception.ServiceException;
import com.pro.util.MD5Util;
import com.pro.util.ResultEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.Random;
@Service
public class MemberServiceImpl implements MemberService {
@Autowired
private MemberMapper memberMapper;
@Override
public Member createMember(String username, String password, String nickName) {
QueryWrapper <Member>wrapper=new QueryWrapper<>();
wrapper.eq("username",username);
List <Member> list = memberMapper.selectList(wrapper);
if (!list.isEmpty()){
//表示已经有了这个会员
throw new ServiceException(ResultEnum.USER_NAME_REGISTERED);
}
//随机生成盐
int salt = new Random().nextInt(1000) + 1000;
String md5pwd = MD5Util.md5Digest(password, salt);
Member member = new Member();
member.setUsername(username);
member.setNickname(nickName);
member.setSalt(salt);
member.setPassword(md5pwd);
member.setCreateTime(new Date());
memberMapper.insert(member);
return member;
}
@Override
public Member checkLogin(String username, String password) {
QueryWrapper<Member> Wrapper = new QueryWrapper<>();
Wrapper.eq("username",username);
Member member = memberMapper.selectOne(Wrapper);
if (member==null){
throw new ServiceException(ResultEnum.USERNAME_NONE);
}
String md5pwd = MD5Util.md5Digest(password, member.getSalt());
if (!md5pwd.equals(member.getPassword())){
throw new ServiceException(ResultEnum.PASSWORD_NOT_TRUE);
}
return member;
}
}
接下来就是会员的控制层了
但是要先说一下ModelAndView
一、ModelAndView概述
简单理解它是将后台返回的数据传递给View层,同时包含一个要访问的View层的URL地址。当控制器处理完请求后,通常控制器会将包含视图名称以及一些模型属性的ModelAndView对象返回给DispatcherServlet。二、ModelAndView的方法
常用ModelAndView方法:addObject():添加模型数据用
setViewName():设置视图
ModelAndView 对象有两个个作用:
- 将底层获取的数据进行封装
将控制器方法中处理的结果数据传递到结果页面,也就是把在结果页面上需要的数据放到ModelAndView对象中即可。
通过key/value的方式绑定数据
modelAndView.addObject(String attributeName, Object attributeValue);
设置跳转地址
ModelAndView view = new ModelAndView("product");//或
modelAndView.setViewName("product");
在springMVC.xml中的配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 给view返回的指定页面名称添加前后缀 -->
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
下面是会员控制层
注入了
@Autowired
private MemberService memberService;
从HttpServletRequest request 中获取到验证码的值 方法是 :
String verifyCode = (String) request.getSession().getAttribute(“verifyCode”); 然后就与用户输入的验证码作比较 相同就进行注册或者登录
HttpSession session 把用户放到session中发送到前端以供使用
package com.pro.controller;
import com.pro.domain.Member;
import com.pro.service.MemberService;
import com.pro.service.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
@Controller
public class MemberController {
@Autowired
private MemberService memberService;
//a标签请求register.html 时会跳转到/reg a标签的请求类型是get
@GetMapping("/register.html")
public ModelAndView genRegister() {
return new ModelAndView("/reg");
}
@PostMapping("/regist")
@ResponseBody
public Map register(String vc, String username, String password, String nickname, HttpServletRequest request) {
Map<String, String> r = new HashMap<>();
//从session 拿到验证码进行验证
String verifyCode = (String) request.getSession().getAttribute("verifyCode");
if (vc == null || verifyCode == null || !vc.equalsIgnoreCase(verifyCode)) {
r.put("code", "900");
r.put("msg", "验证码错误");
} else {
//验证成功后
try {
Member member = memberService.createMember(username, password, nickname);
r.put("code", "200");
r.put("msg", "register ok !");
} catch (ServiceException e) {
r.put("msg", e.getMsg());
r.put("code", e.getCode());
}
}
return r;
}
//去登录页面
@GetMapping("/login.html")
public ModelAndView toLogin(){
return new ModelAndView("/login");
}
@PostMapping("/checkLogin")
@ResponseBody
public Map checkLogin(String username, String password, String vc, HttpSession session){
String verifyCode = (String) session.getAttribute("verifyCode");
Map<String,String> r=new HashMap<>();
if (vc==null||verifyCode==null||!vc.equalsIgnoreCase(vc)){
r.put("code","901");
r.put("msg","验证码错误");
}
else {
try {
Member member = memberService.checkLogin(username, password);
session.setAttribute("member",member);
r.put("code","200");
r.put("msg","登录成功");
} catch (ServiceException e) {
r.put("code",e.getCode());
r.put("msg",e.getMsg());
}
}
return r;
}
}
下面就是书的评价的获取方法
在
EvaluationServiceImpl
类中,主要完成了以下工作:
- 注入了
EvaluationMapper
、MemberMapper
和BookMapper
,使得可以通过这些 mapper 访问数据库。- 在
getEvaluationByBookId
方法中,根据给定的bookId
查询符合条件的评价列表。首先创建了一个QueryWrapper
对象,使用eq
方法设置了查询条件book_id = bookId
和state = "enable"
,并按照create_time
字段降序排列。然后通过bookMapper
根据bookId
查询对应的Book
对象。- 执行查询操作,将查询结果存放在
list
中。接着通过循环遍历list
,并根据每个评价对象的memberId
查询对应的Member
对象,并将查询到的Book
和Member
对象分别设置到评价对象中。- 最后返回装有所有评价对象的
list
。代码的作用是根据给定的
bookId
查询对应书籍的评价列表,并将每个评价对象与对应的书籍和会员进行关联。
package com.pro.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pro.domain.Book;
import com.pro.domain.Evaluation;
import com.pro.domain.Member;
import com.pro.mapper.BookMapper;
import com.pro.mapper.EvaluationMapper;
import com.pro.mapper.MemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EvaluationServiceImpl implements EvaluationService {
@Autowired
private EvaluationMapper evaluationMapper;
@Autowired
private MemberMapper memberMapper;
@Autowired
private BookMapper bookMapper;
//评论列表
@Override
public List<Evaluation> getEvaluationByBookId(Long bookId) {
QueryWrapper<Evaluation> wrapper = new QueryWrapper<>();
wrapper.eq("book_id", bookId);//where id=?
wrapper.eq("state", "enable");
wrapper.orderByDesc("create_time");
Book book = bookMapper.selectById(bookId);
List<Evaluation> list = evaluationMapper.selectList(wrapper);
for (Evaluation evaluation : list) {
Member member = memberMapper.selectById(evaluation.getMemberId());
evaluation.setBook(book);
evaluation.setMember(member);
}
//循环过后评论集合中的对象就都有了
return list;
}
}
前端
这是从category 集合便利 分类列表 列表就如下图
<#list list 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>
定义 art 模板
<#--定义 art 模板-->
<script type="text/html" id="tp1">
<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="./resources/images/{{cover}}">
</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>
这是登录页面的js
<script type="text/javascript">
/*初始化星星图片路径*/
$.fn.raty.defaults.path="./resources/raty/lib/images";
$(function () {
loadNext(true);
$("#btnMore").click(function () {
loadNext();
});
//类别高亮
$(".category").click(function () {
$(".category").removeClass("highlight");
$(".category").addClass("text-black-50");
$(this).addClass("highlight");
//设置类别 <input type="hidden" id="categoryId" value="-1"> 的值
//data("category")就是他得知
let categoryId = $(this).data("category");
$("#categoryId").val(categoryId);
loadNext(true);
});
//热度和评分高亮
$(".order").click(function () {
$(".order").removeClass("highlight");
$(".order").addClass("text-black-50");
$(this).addClass("highlight");
let order = $(this).data("order");
$("#order").val(order);
loadNext(true);
});
});
function loadNext(isFirst){
//如果是初次加载,isFirst 为true
if (isFirst==true){
$("#bookList").html("");
$("#nextPage").val(1);
}
//将按钮单击事件内的代码放到这里
let nextPage = $("#nextPage").val();
let categoryId = $("#categoryId").val();
let order = $("#order").val();
$.ajax({
url: 'books',
data:{"page":nextPage,"categoryId":categoryId,"order":order},
type: 'get',
dataType: 'json',
success: function (info) {
console.log(info);
let bookList = info.records;
for (const book of bookList) {
// var html = "<li>" + book.bookName + "</li>"
let html = template('tp1', book);
$("#bookList").append(html);//追加图书列表
}
$(".stars").raty({readOnly:true});
if (info.current<info.pages){
$("#nextPage").val(parseInt(info.current)+1);//把当前页码加一
$("#btnMore").show();
$("#divNoMore").hide();//让div隐藏
}else {
$("#btnMore").hide();
$("#divNoMore").show();//显示
}
}
})
}
</script>
登录的 js
<script>
function reloadVerifyCode() {
$("#imgVerifyCode").attr("src", "verify_code?tp=" + new Date().getTime());//时间戳骗过缓存
}
$(function () {
$("#imgVerifyCode").click(function () {//点击验证码时的事件
reloadVerifyCode();// 刷新验证码
});
$("#btnSubmit").click(function () {
$.ajax({
url:'checkLogin',
type:'post',
dataType:'json',
data:$("#frmLogin").serialize(),//序列化表单曁获取数据
success:function (data) {
console.log(data)
if (data.code=="200"){
window.location='/xzw?ts='+new Date().getTime();
}
}
});
});
});
</script>
注册的js
<script type="text/javascript">
function reloadVerifyCode() {
$("#imgVerifyCode").attr("src", "verify_code?tp=" + new Date().getTime());//时间戳骗过缓存
}
$(function () {
$("#imgVerifyCode").click(function () {
reloadVerifyCode();
});
$("#btnSubmit").click(function () {
//进行验证
let username = $.trim($("#username").val());
let regex = /^.{6,10}$/; //正则表达式
if (!regex.test(username)) {
alert("用户名请输入正确格式(6-10)位 ");
return
}
let password = $.trim($("#password").val());//去掉空格
if (!regex.test(password)) {
alert("密码请输入正确格式(6-10)位 ");
return
}
//拿到当前按钮的对象
let $btnReg = $(this);
$btnReg.text("正在处理...");
$btnReg.attr("disabled", "disabled");
$.ajax({
url: 'regist',
type: 'post',
dataType: 'json',
data: $("#frmLogin").serialize(),
success: function (data) {
console.log("服务器相应", data);
if (data.code == 200) {
$("#exampleModalCenter").modal({});
$("#exampleModalCenter").modal("show");
} else {
reloadVerifyCode();
$btnReg.text("注 册")
$btnReg.attr("disabled", false);
alert(data.code+","+data.msg)
}
}
});
});
});
</script>
获取短评 通过evList as evaluation
<div class="reply pl-2 pr-2">
<#list evList 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://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg3.doubanio.com%2Ff%2Ftalion%2F7a0756b3b6e67b59ea88653bc0cfa14f61ff219d%2Fpics%2Fcard%2Fic_like_gray.svg&pos_id=img-RVGXTkg8-1697856670430)"/>
<span>${evaluation.enjoy}</span><!-- 点赞数量 -->
</button>
</div><!-- 短评内容-->
<div class="row mt-2 small mb-3">
${evaluation.content}
</div>
<hr/>
</div>
<!-- 所有评论。循环遍历--->
</#list>
</div>