文章目录
- 常用maven 包
- yml
- mvc 配置
- swagger
- bean 配置
- websocket
- 用户实体类
- DTO
- enum
- base repository
- impl repository
- UserRepository
- repostiory
- CrudService
- AbstractCrudService
- UserService
- UserServiceImpl
- UserController
- 登录拦截器
- AOP
- html
- 日志配置文件

常用maven 包
<!--webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.34</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>js-cookie</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.2.1</version>
</dependency>
<!--redis 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<!--start 依赖-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--test类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--netty 导入-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty-all.version}</version>
</dependency>
<!--WEB socket 对象-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- RabbitMQ Starter Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--netty 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- Spring Data JPA 依赖 :: 数据持久层框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- h2 数据源依赖 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<!--模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<!--热部署依赖-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-cron</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
yml
server:
port: 3000
undertow:
io-threads: 2
worker-threads: 36
buffer-size: 1024
directBuffers: true
servlet:
session:
timeout: 86400s
logging:
file: ./logs/log.log
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/theatre?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
druid:
validation-query: SELECT 1
initial-size: 1
min-idle: 1
max-active: 20
filters: stat
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 3000000
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-open-prepared-statements: 20
redis:
host: 127.0.0.1
password:
port: 6379
timeout: 12000
database: 0
lettuce:
pool:
max-active: 300
max-idle: 100
min-idle: 8
max-wait: -1
thymeleaf:
mode: HTML
prefix: classpath:/templates/
suffix: .html
encoding: UTF-8
servlet:
content-type: text/html
cache: false
enabled: true
messages:
basename: i18n/messages
encoding: UTF-8
jpa:
hibernate:
show-sql: true
ddl-auto: update
swagger:
base
#debug: true
mvc 配置
package com.devin.config;
import com.devin.web.interceptor.LocaleInterceptor;
import com.devin.web.interceptor.LoginInterceptor;
import org.apache.catalina.filters.RequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.resource.PathResourceResolver;
import org.springframework.web.servlet.resource.WebJarsResourceResolver;
import java.util.Locale;
/**
* User: devin
* Date: 2020/2/19
* Time: 19:27
* Description: No Description
*/
public class SpringMvcConfig implements WebMvcConfigurer {
private LocaleInterceptor localeInterceptor;
private LoginInterceptor loginInterceptor;
/**
* 注册拦截器
*
* @param registry registry
*/
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/user.*")
.addPathPatterns("/user/**")
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/getLogin")
.excludePathPatterns("/static/**");
registry.addInterceptor(localeInterceptor)
.addPathPatterns("/user.*")
.addPathPatterns("/user/**")
.addPathPatterns("/install");
registry.addInterceptor(localeChangeInterceptor())
.addPathPatterns("/install");
}
/**
* 配置静态资源路径
*
* @param registry registry
*/
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/templates/**");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/").resourceChain(false)
.addResolver(new WebJarsResourceResolver())
.addResolver(new PathResourceResolver());;
}
/**
* 国际化设置
*
* @return LocaleResolver
*/
public LocaleResolver localeResolver() {
final SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.CHINA);
return slr;
}
/**
* 国际化参数拦截器
*
* @return LocaleChangeInterceptor
*/
public LocaleChangeInterceptor localeChangeInterceptor() {
final LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
}
swagger
package com.devin.config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author: simpl
* @date: 2020/2/27
* @time: 10:17
* @description:
*/
public class SwaggerConfig {
public Docket apiConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.devin.web.controller"))
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.contact(new Contact("devin","","1241169737@"))
.title("剧场接口文档")
.version("v1.0.0")
.build();
}
}
bean 配置
package com.devin.config;
import com.devin.util.JwtUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
/**
* @author: simpl
* @date: 2020/3/16
* @time: 13:02
* @description:
*/
public class BeanConfig {
public JwtUtil jjwtUtil() {
return JwtUtil.getInstance();
}
/**
* 配置redisTemplate
* @param lettuceConnectionFactory lettuceConnection工厂
* @return 配置成功返回可以使用RedisTemplate<String, Object> 类型
*/
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// 设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
// key序列化
redisTemplate.setKeySerializer(stringSerializer);
// value序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// Hash key序列化
redisTemplate.setHashKeySerializer(stringSerializer);
// Hash value序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
websocket
package com.devin.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @author heenanagpal
*
*/
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/channel");
}
}
用户实体类
package com.devin.model.domain;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
* @author: simpl
* @date: 2020/3/13
* @time: 20:23
* @description:
*/
(name = "theatre_movie")
public class Movie {
private static final long serialVersionUID = -5144055068797033748L;
/*
* 电影的id
* */
(strategy = GenerationType.IDENTITY)
private long id;
/*
* 影片类型
* */
private String kind;
/*
* 电影名字
* */
private String title;
/*
* 创建日期
* */
private Date createDate;
/*
* 电影的链接
* */
private String url;
/*
* 电影的封面
* */
private String faceUrl;
/*
* 上传者名字
* */
private String createName;
}
DTO
public class ChatMessage {
public enum MessageType {
CHAT, JOIN, LEAVE
}
private MessageType messageType;
private String content;
private String sender;
public MessageType getType() {
return messageType;
}
public void setType(MessageType messageType) {
this.messageType = messageType;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
}
enum
package com.devin.model.enums;
/**
* <pre>
* 文章类型enum
* </pre>
*
* @author : RYAN0UP
* @date : 2018/7/1
*/
public enum PostTypeEnum {
/**
* 文章
*/
POST_TYPE_POST("post"),
/**
* 页面
*/
POST_TYPE_PAGE("page");
private String desc;
PostTypeEnum(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
base repository
package com.devin.repository.base;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.lang.NonNull;
import java.util.List;
/**
* Base repository interface contains some common methods.
*
* @param <DOMAIN> doamin type
* @param <ID> id type
* @author johnniang
*/
public interface BaseRepository<DOMAIN, ID> extends JpaRepository<DOMAIN, ID> {
/**
* Finds all domain by id list and the specified sort.
*
* @param ids id list of domain must not be null
* @param sort the specified sort must not be null
* @return a list of domains
*/
List<DOMAIN> findAllByIdIn( Iterable<ID> ids, Sort sort);
/**
* Deletes by id list.
*
* @param ids id list of domain must not be null
* @return number of rows affected
*/
long deleteByIdIn( Iterable<ID> ids);
}
impl repository
- 需要在启动类加 否则实体管理器无法启动
@EnableJpaAuditing @EnableJpaRepositories(basePackages = "com.devin.repository", repositoryBaseClass = BaseRepositoryImpl.class)
package com.devin.repository.base;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Implementation of base repository.
*
* @param <DOMAIN> domain type
* @param <ID> id type
* @author johnniang
*/
public class BaseRepositoryImpl<DOMAIN, ID> extends SimpleJpaRepository<DOMAIN, ID> implements BaseRepository<DOMAIN, ID> {
// private final Logger log = Logger.getLogger(getClass());
private final JpaEntityInformation<DOMAIN, ID> entityInformation;
private final EntityManager entityManager;
public BaseRepositoryImpl(JpaEntityInformation<DOMAIN, ID> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityInformation = entityInformation;
this.entityManager = entityManager;
}
/**
* Finds all domain by id list and the specified sort.
*
* @param ids id list of domain must not be null
* @param sort the specified sort must not be null
* @return a list of domains
*/
public List<DOMAIN> findAllByIdIn(Iterable<ID> ids, Sort sort) {
Assert.notNull(ids, "The given Iterable of Id's must not be null!");
log.debug("Customized findAllById method was invoked");
if (!ids.iterator().hasNext()) {
return Collections.emptyList();
}
if (!this.entityInformation.hasCompositeId()) {
ByIdsSpecification<DOMAIN> specification = new ByIdsSpecification<>(this.entityInformation);
TypedQuery<DOMAIN> query = super.getQuery(specification, sort);
return query.setParameter(specification.parameter, ids).getResultList();
} else {
List<DOMAIN> results = new ArrayList<>();
ids.forEach(id -> super.findById(id).ifPresent(results::add));
return results;
}
}
/**
* Deletes by id list.
*
* @param ids id list of domain must not be null
* @return number of rows affected
*/
public long deleteByIdIn(Iterable<ID> ids) {
log.debug("Customized deleteByIdIn method was invoked");
// Find all domains
List<DOMAIN> domains = findAllById(ids);
// Delete in batch
deleteInBatch(domains);
// Return the size of domain deleted
return domains.size();
}
private static final class ByIdsSpecification<T> implements Specification<T> {
private static final long serialVersionUID = 1L;
private final JpaEntityInformation<T, ?> entityInformation;
ParameterExpression<Iterable> parameter;
ByIdsSpecification(JpaEntityInformation<T, ?> entityInformation) {
this.entityInformation = entityInformation;
}
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<?> path = root.get(this.entityInformation.getIdAttribute());
this.parameter = cb.parameter(Iterable.class);
return path.in(this.parameter);
}
}
}
UserRepository
package cc.ryanc.halo.repository;
import cc.ryanc.halo.model.domain.User;
import cc.ryanc.halo.repository.base.BaseRepository;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* <pre>
* 用户持久层
* </pre>
*
* @author : RYAN0UP
* @date : 2017/11/14
*/
public interface UserRepository extends BaseRepository<User, Long> {
/**
* 根据用户名和密码查询
*
* @param userName userName
* @param userPass userPass
* @return User
*/
User findByUserNameAndUserPass(String userName, String userPass);
/**
* 根据邮箱和密码查询
*
* @param userEmail userEmail
* @param userPass userPass
* @return User
*/
User findByUserEmailAndUserPass(String userEmail, String userPass);
/**
* 根据用户编号和密码查询
*
* @param userId userId
* @param userPass userpass
* @return User
*/
User findByUserIdAndUserPass(Long userId, String userPass);
}
repostiory
package com.devin.repository;
import com.devin.model.domain.User;
import com.devin.repository.base.BaseRepository;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* <pre>
* 用户持久层
* </pre>
*
* @author : RYAN0UP
* @date : 2017/11/14
*/
public interface UserRepository extends BaseRepository<User, Long> {
/**
* 根据用户名和密码查询
*
* @param userName userName
* @param userPass userPass
* @return User
*/
User findByUserNameAndUserPass(String userName, String userPass);
/**
* 根据邮箱和密码查询
*
* @param userEmail userEmail
* @param userPass userPass
* @return User
*/
User findByUserEmailAndUserPass(String userEmail, String userPass);
/**
* 根据用户编号和密码查询
*
* @param userId userId
* @param userPass userpass
* @return User
*/
User findByUserIdAndUserPass(Long userId, String userPass);
}
CrudService
package cc.ryanc.halo.service.base;
import cc.ryanc.halo.exception.NotFoundException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
/**
* CrudService interface contains some common methods.
*
* @param <DOMAIN> domain type
* @param <ID> id type
* @author johnniang
*/
public interface CrudService<DOMAIN, ID> {
/**
* List All
*
* @return List
*/
List<DOMAIN> listAll();
/**
* List all by sort
*
* @param sort sort
* @return List
*/
List<DOMAIN> listAll( Sort sort);
/**
* List all by pageable
*
* @param pageable pageable
* @return Page
*/
Page<DOMAIN> listAll( Pageable pageable);
/**
* List all by ids
*
* @param ids ids
* @return List
*/
List<DOMAIN> listAllByIds( Collection<ID> ids);
/**
* List all by ids and sort
*
* @param ids ids
* @param sort sort
* @return List
*/
List<DOMAIN> listAllByIds( Collection<ID> ids, Sort sort);
/**
* Fetch by id
*
* @param id id
* @return Optional
*/
Optional<DOMAIN> fetchById( ID id);
/**
* Get by id
*
* @param id id
* @return DOMAIN
* @throws NotFoundException If the specified id does not exist
*/
DOMAIN getById( ID id);
/**
* Gets domain of nullable by id.
*
* @param id id
* @return DOMAIN
*/
DOMAIN getByIdOfNullable( ID id);
/**
* Exists by id.
*
* @param id id
* @return boolean
*/
boolean existsById( ID id);
/**
* Must exist by id, or throw NotFoundException.
*
* @param id id
* @throws NotFoundException If the specified id does not exist
*/
void mustExistById( ID id);
/**
* count all
*
* @return long
*/
long count();
/**
* save by domain
*
* @param domain domain
* @return DOMAIN
*/
DOMAIN create( DOMAIN domain);
/**
* save by domains
*
* @param domains domains
* @return List
*/
List<DOMAIN> createInBatch( Collection<DOMAIN> domains);
/**
* Updates by domain
*
* @param domain domain
* @return DOMAIN
*/
DOMAIN update( DOMAIN domain);
/**
* Updates by domains
*
* @param domains domains
* @return List
*/
List<DOMAIN> updateInBatch( Collection<DOMAIN> domains);
/**
* Removes by id
*
* @param id id
* @return DOMAIN
* @throws NotFoundException If the specified id does not exist
*/
DOMAIN removeById( ID id);
/**
* Removes by id if present.
*
* @param id id
* @return DOMAIN
*/
DOMAIN removeByIdOfNullable( ID id);
/**
* Remove by domain
*
* @param domain domain
*/
void remove( DOMAIN domain);
/**
* Remove by ids
*
* @param ids ids
*/
void removeInBatch( Collection<ID> ids);
/**
* Remove all by domains
*
* @param domains domains
*/
void removeAll( Collection<DOMAIN> domains);
/**
* Remove all
*/
void removeAll();
}
AbstractCrudService
package cc.ryanc.halo.service.base;
import cc.ryanc.halo.exception.NotFoundException;
import cc.ryanc.halo.logging.Logger;
import cc.ryanc.halo.repository.base.BaseRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* Abstract service implementation.
*
* @param <DOMAIN> domain type
* @param <ID> id type
* @author johnniang
*/
public abstract class AbstractCrudService<DOMAIN, ID> implements CrudService<DOMAIN, ID> {
private final Logger log = Logger.getLogger(getClass());
private final String domainName;
private final BaseRepository<DOMAIN, ID> repository;
protected AbstractCrudService(BaseRepository<DOMAIN, ID> repository) {
this.repository = repository;
// Get domain name
Class<DOMAIN> domainClass = (Class<DOMAIN>) fetchType(0);
domainName = domainClass.getSimpleName();
}
/**
* Gets actual generic type.
*
* @param index generic type index
* @return real generic type will be returned
*/
private Type fetchType(int index) {
Assert.isTrue(index >= 0 && index <= 1, "type index must be between 0 to 1");
return ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[index];
}
/**
* List All
*
* @return List
*/
public List<DOMAIN> listAll() {
return repository.findAll();
}
/**
* List all by sort
*
* @param sort sort
* @return List
*/
public List<DOMAIN> listAll(Sort sort) {
Assert.notNull(sort, "Sort info must not be null");
return repository.findAll(sort);
}
/**
* List all by pageable
*
* @param pageable pageable
* @return Page
*/
public Page<DOMAIN> listAll(Pageable pageable) {
Assert.notNull(pageable, "Pageable info must not be null");
return repository.findAll(pageable);
}
/**
* List all by ids
*
* @param ids ids
* @return List
*/
public List<DOMAIN> listAllByIds(Collection<ID> ids) {
return CollectionUtils.isEmpty(ids) ? Collections.emptyList() : repository.findAllById(ids);
}
/**
* List all by ids and sort
*
* @param ids ids
* @param sort sort
* @return List
*/
public List<DOMAIN> listAllByIds(Collection<ID> ids, Sort sort) {
Assert.notNull(sort, "Sort info must not be null");
return CollectionUtils.isEmpty(ids) ? Collections.emptyList() : repository.findAllByIdIn(ids, sort);
}
/**
* Fetch by id
*
* @param id id
* @return Optional
*/
public Optional<DOMAIN> fetchById(ID id) {
Assert.notNull(id, domainName + " id must not be null");
return repository.findById(id);
}
/**
* Get by id
*
* @param id id
* @return DOMAIN
* @throws NotFoundException If the specified id does not exist
*/
public DOMAIN getById(ID id) {
return fetchById(id).orElseThrow(() -> new NotFoundException(domainName + " was not found"));
}
/**
* Gets domain of nullable by id.
*
* @param id id
* @return DOMAIN
*/
public DOMAIN getByIdOfNullable(ID id) {
return fetchById(id).orElse(null);
}
/**
* Exists by id.
*
* @param id id
* @return boolean
*/
public boolean existsById(ID id) {
Assert.notNull(id, domainName + " id must not be null");
return repository.existsById(id);
}
/**
* Must exist by id, or throw NotFoundException.
*
* @param id id
* @throws NotFoundException If the specified id does not exist
*/
public void mustExistById(ID id) {
if (!existsById(id)) {
throw new NotFoundException(domainName + " was not exist");
}
}
/**
* count all
*
* @return long
*/
public long count() {
return repository.count();
}
/**
* save by domain
*
* @param domain domain
* @return DOMAIN
*/
public DOMAIN create(DOMAIN domain) {
Assert.notNull(domain, domainName + " data must not be null");
return repository.save(domain);
}
/**
* save by domains
*
* @param domains domains
* @return List
*/
public List<DOMAIN> createInBatch(Collection<DOMAIN> domains) {
return CollectionUtils.isEmpty(domains) ? Collections.emptyList() : repository.saveAll(domains);
}
/**
* Updates by domain
*
* @param domain domain
* @return DOMAIN
*/
public DOMAIN update(DOMAIN domain) {
Assert.notNull(domain, domainName + " data must not be null");
return repository.saveAndFlush(domain);
}
/**
* Updates by domains
*
* @param domains domains
* @return List
*/
public List<DOMAIN> updateInBatch(Collection<DOMAIN> domains) {
return CollectionUtils.isEmpty(domains) ? Collections.emptyList() : repository.saveAll(domains);
}
/**
* Removes by id
*
* @param id id
* @return DOMAIN
* @throws NotFoundException If the specified id does not exist
*/
public DOMAIN removeById(ID id) {
// Get non null domain by id
DOMAIN domain = getById(id);
// Remove it
remove(domain);
// return the deleted domain
return domain;
}
/**
* Removes by id if present.
*
* @param id id
* @return DOMAIN
*/
public DOMAIN removeByIdOfNullable(ID id) {
return fetchById(id).map(domain -> {
remove(domain);
return domain;
}).orElse(null);
}
/**
* Remove by domain
*
* @param domain domain
*/
public void remove(DOMAIN domain) {
Assert.notNull(domain, domainName + " data must not be null");
repository.delete(domain);
}
/**
* Remove by ids
*
* @param ids ids
*/
public void removeInBatch(Collection<ID> ids) {
if (CollectionUtils.isEmpty(ids)) {
log.warn(domainName + " id collection is empty");
return;
}
repository.deleteByIdIn(ids);
}
/**
* Remove all by domains
*
* @param domains domains
*/
public void removeAll(Collection<DOMAIN> domains) {
if (CollectionUtils.isEmpty(domains)) {
log.warn(domainName + " collection is empty");
return;
}
repository.deleteInBatch(domains);
}
/**
* Remove all
*/
public void removeAll() {
repository.deleteAll();
}
}
UserService
package cc.ryanc.halo.service;
import cc.ryanc.halo.model.domain.User;
import cc.ryanc.halo.service.base.CrudService;
import java.util.Date;
/**
* <pre>
* 用户业务逻辑接口
* </pre>
*
* @author : RYAN0UP
* @date : 2017/11/14
*/
public interface UserService extends CrudService<User, Long> {
/**
* 根据用户名和密码查询,用于登录
*
* @param userName userName
* @param userPass userPass
* @return User
*/
User userLoginByName(String userName, String userPass);
/**
* 根据邮箱和密码查询,用户登录
*
* @param userEmail userEmail
* @param userPass userPass
* @return User
*/
User userLoginByEmail(String userEmail, String userPass);
/**
* 查询所有用户
*
* @return User
*/
User findUser();
/**
* 根据用户编号和密码查询
*
* @param userId userid
* @param userPass userpass
* @return User
*/
User findByUserIdAndUserPass(Long userId, String userPass);
/**
* 修改禁用状态
*
* @param enable enable
*/
void updateUserLoginEnable(String enable);
/**
* 修改最后登录时间
*
* @param lastDate 最后登录时间
* @return User
*/
User updateUserLoginLast(Date lastDate);
/**
* 增加登录错误次数
*
* @return 登录错误次数
*/
Integer updateUserLoginError();
/**
* 修改用户的状态为正常
*
* @return User
*/
User updateUserNormal();
}
UserServiceImpl
package cc.ryanc.halo.service.impl;
import cc.ryanc.halo.model.domain.User;
import cc.ryanc.halo.model.enums.TrueFalseEnum;
import cc.ryanc.halo.repository.UserRepository;
import cc.ryanc.halo.service.UserService;
import cc.ryanc.halo.service.base.AbstractCrudService;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* <pre>
* 用户业务逻辑实现类
* </pre>
*
* @author : RYAN0UP
* @date : 2017/11/14
*/
public class UserServiceImpl extends AbstractCrudService<User, Long> implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
super(userRepository);
this.userRepository = userRepository;
}
/**
* 根据用户名和密码查询
*
* @param userName userName
* @param userPass userPass
* @return User
*/
public User userLoginByName(String userName, String userPass) {
return userRepository.findByUserNameAndUserPass(userName, userPass);
}
/**
* 根据邮箱和密码查询,用户登录
*
* @param userEmail userEmail
* @param userPass userPass
* @return User
*/
public User userLoginByEmail(String userEmail, String userPass) {
return userRepository.findByUserEmailAndUserPass(userEmail, userPass);
}
/**
* 查询所有用户
*
* @return User
*/
public User findUser() {
final List<User> users = userRepository.findAll();
if (users != null && users.size() > 0) {
return users.get(0);
} else {
return new User();
}
}
/**
* 验证修改密码时,密码是否正确
*
* @param userId userId
* @param userPass userPass
* @return User
*/
public User findByUserIdAndUserPass(Long userId, String userPass) {
return userRepository.findByUserIdAndUserPass(userId, userPass);
}
/**
* 修改禁用状态
*
* @param enable enable
*/
public void updateUserLoginEnable(String enable) {
final User user = this.findUser();
user.setLoginError(0);
user.setLoginEnable(enable);
// Update user
update(user);
}
/**
* 修改最后登录时间
*
* @param lastDate 最后登录时间
* @return User
*/
public User updateUserLoginLast(Date lastDate) {
final User user = this.findUser();
user.setLoginLast(lastDate);
// Update user
return update(user);
}
/**
* 增加登录错误次数
*
* @return 登录错误次数
*/
public Integer updateUserLoginError() {
final User user = this.findUser();
user.setLoginError((user.getLoginError() == null ? 0 : user.getLoginError()) + 1);
// Update user
update(user);
// Return login error times
return user.getLoginError();
}
/**
* 修改用户的状态为正常
*
* @return User
*/
public User updateUserNormal() {
final User user = this.findUser();
user.setLoginEnable(TrueFalseEnum.TRUE.getDesc());
user.setLoginError(0);
user.setLoginLast(new Date());
return update(user);
}
}
UserController
package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.model.domain.User;
import cc.ryanc.halo.model.dto.JsonResult;
import cc.ryanc.halo.model.enums.ResultCodeEnum;
import cc.ryanc.halo.service.UserService;
import cc.ryanc.halo.utils.LocaleMessageUtil;
import cn.hutool.crypto.SecureUtil;
import freemarker.template.Configuration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import static cc.ryanc.halo.model.dto.HaloConst.USER_SESSION_KEY;
/**
* <pre>
* 后台用户管理控制器
* </pre>
*
* @author : RYAN0UP
* @date : 2017/12/24
*/
(value = "/admin/profile")
public class UserController {
private UserService userService;
private Configuration configuration;
private LocaleMessageUtil localeMessageUtil;
/**
* 获取用户信息并跳转
*
* @return 模板路径admin/admin_profile
*/
public String profile() {
return "admin/admin_profile";
}
/**
* 处理修改用户资料的请求
*
* @param user user
* @param session session
* @return JsonResult
*/
(value = "save")
public JsonResult saveProfile( User user, BindingResult result, HttpSession session) {
try {
if (result.hasErrors()) {
for (ObjectError error : result.getAllErrors()) {
return new JsonResult(ResultCodeEnum.FAIL.getCode(), error.getDefaultMessage());
}
}
userService.create(user);
configuration.setSharedVariable("user", userService.findUser());
session.removeAttribute(USER_SESSION_KEY);
} catch (Exception e) {
log.error("Failed to modify user profile: {}", e.getMessage());
return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.common.edit-failed"));
}
return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), localeMessageUtil.getMessage("code.admin.common.edit-success"));
}
/**
* 处理修改密码的请求
*
* @param beforePass 旧密码
* @param newPass 新密码
* @param userId 用户编号
* @param session session
* @return JsonResult
*/
(value = "changePass")
public JsonResult changePass(("beforePass") String beforePass,
("newPass") String newPass,
("userId") Long userId,
HttpSession session) {
try {
final User user = userService.findByUserIdAndUserPass(userId, SecureUtil.md5(beforePass));
if (null != user) {
user.setUserPass(SecureUtil.md5(newPass));
userService.update(user);
session.removeAttribute(USER_SESSION_KEY);
} else {
return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.user.old-password-error"));
}
} catch (Exception e) {
log.error("Password change failed: {}", e.getMessage());
return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.user.update-password-failed"));
}
return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), localeMessageUtil.getMessage("code.admin.user.update-password-success"));
}
}
登录拦截器
package com.devin.web.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static com.devin.model.dto.TheatreConst.USER_SESSION_KEY;
/**
* <pre>
* 后台登录控制器
* </pre>
*
* @author : RYAN0UP
* @date : 2017/12/13
*/
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final Object obj = request.getSession().getAttribute(USER_SESSION_KEY);
//如果user不为空则放行
log.info("进入登入拦截器");
if (null != obj) {
log.info("拦截器返回true");
return true;
}
//否则拦截并跳转到登录
log.info("拦截器返回false");
response.sendRedirect("/user/login");
return false;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
AOP
package com.devin.aop;
import com.devin.model.enums.DateStyleEnum;
import com.devin.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
public class ReControllerLogAdvice {
/** 进入方法时间戳 */
private Long startTime;
/** 方法结束时间戳(计时) */
private Long endTime;
public ReControllerLogAdvice() {}
/**
* 定义请求日志切入点,其切入点表达式有多种匹配方式,这里是指定路径
*/
("execution(public * com.devin.web.*.*.*(..))")
public void webLogPointcut() {}
/**
* 前置通知:
* 1. 在执行目标方法之前执行,比如请求接口之前的登录验证;
* 2. 在前置通知中设置请求日志信息,如开始时间,请求参数,注解内容等
*
* @param joinPoint 切点
*/
("webLogPointcut()")
public void doBefore(JoinPoint joinPoint) {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
//打印请求的内容
startTime = System.currentTimeMillis();
log.info("请求开始时间:{}", DateUtil.formatDate(new Date(), DateStyleEnum.yyyy_MM_dd_HH_mm_ss));
log.info("请求Url : {}", request.getRequestURL().toString());
log.info("请求方式 : {}", request.getMethod());
log.info("请求ip : {}", request.getRemoteAddr());
log.info("请求方法 : {}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
}
/**
* 返回通知:
* 1. 在目标方法正常结束之后执行
* 1. 在返回通知中补充请求日志信息,如返回时间,方法耗时,返回值,并且保存日志信息
*
* @param ret 返回参数
*/
(returning = "ret", pointcut = "webLogPointcut()")
public void doAfterReturning(Object ret) {
endTime = System.currentTimeMillis();
log.info("请求结束时间:{}", DateUtil.formatDate(new Date(), DateStyleEnum.yyyy_MM_dd_HH_mm_ss));
log.info("请求耗时:{}", (endTime - startTime));
// 处理完请求,返回内容
log.info("请求返回 : {}", ret);
}
/**
* 异常通知:
* 1. 在目标方法非正常结束,发生异常或者抛出异常时执行
* 1. 在异常通知中设置异常信息,并将其保存
*
* @param throwable 异常
*/
(value = "webLogPointcut()", throwing = "throwable")
public void doAfterThrowing(Throwable throwable) {
// 保存异常日志记录
log.error("发生异常时间:{}", DateUtil.formatDate(new Date(), DateStyleEnum.yyyy_MM_dd_HH_mm_ss));
log.error("抛出异常:{}", throwable.getMessage());
}
}
html
<html xmlns:th="http://www.thymeleaf.org">
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<title>Spring Boot WebSocket Chat Application | CalliCoder</title>
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/webjars/bootstrap/4.4.1-1/css/bootstrap.min.css" />
</head>
<body>
<noscript>
<h2>Sorry! Your browser doesn't support Javascript</h2>
</noscript>
<div id="username-page">
<div class="username-page-container">
<h1 class="title">Type your username</h1>
<form id="usernameForm" name="usernameForm">
<div class="form-group">
<input type="text" id="name" placeholder="Username" autocomplete="off" class="form-control" />
</div>
<div class="form-group">
<button type="submit" class="accent username-submit">Start Chatting</button>
</div>
</form>
</div>
</div>
<div id="chat-page" class="hidden">
<div class="chat-container">
<div class="chat-header">
<h2>Spring WebSocket Chat Demo</h2>
</div>
<div class="connecting">
Connecting...
</div>
<ul id="messageArea">
</ul>
<form id="messageForm" name="messageForm" nameForm="messageForm">
<div class="form-group">
<div class="input-group clearfix">
<input type="text" id="message" placeholder="Type a message..." autocomplete="off" class="form-control"/>
<button type="submit" class="primary">Send</button>
</div>
</div>
</form>
</div>
</div>static
<!--导入可以不用 模板的语法 静态配置了 这里就不需要/static-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script src="/js/main.js"></script>
<script src="webjars/layui/src/layui.js"></script>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/webjars/js-cookie/js.cookie.js"></script>
<script src="index.js" type="text/javascript" ></script>
<script src="js/main.js" type="text/javascript" ></script>
</body>
</html>
日志配置文件
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="/home/ljt/webfile/re/log" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--<withJansi>true</withJansi>-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%red([%d{yyyy-MM-dd HH:mm:ss}]) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{50}) - %cyan(%msg) %n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数-->
<maxHistory>30</maxHistory>
<maxFileSize>20MB</maxFileSize>
<totalSizeCap>2GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>[%d{yyyy-MM-dd HH:mm:ss}] [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 配置mybatis的日志输出级别为INFO -->
<logger name="mybatis.sql" level="INFO"/>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
</configuration>
















