Compare commits
No commits in common. 'yjc' and 'main' have entirely different histories.
@ -1,38 +0,0 @@
|
|||||||
target/
|
|
||||||
!.mvn/wrapper/maven-wrapper.jar
|
|
||||||
!**/src/main/**/target/
|
|
||||||
!**/src/test/**/target/
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
|
|
||||||
### Eclipse ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
build/
|
|
||||||
!**/src/main/**/build/
|
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
### Mac OS ###
|
|
||||||
.DS_Store
|
|
||||||
/.logs/
|
|
||||||
/.idea/
|
|
||||||
.logs
|
|
||||||
.idea
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
<?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>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>2.7.18</version>
|
|
||||||
<relativePath/>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>com.example</groupId>
|
|
||||||
<artifactId>registration</artifactId>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<name>registration</name>
|
|
||||||
<description>培训报名系统</description>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<java.version>1.8</java.version>
|
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
|
||||||
<knife4j.version>4.3.0</knife4j.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<!-- Spring Boot Starter Web -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- MySQL Connector -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>mysql</groupId>
|
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
|
||||||
<version>8.0.33</version>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Mybatis-Plus -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.baomidou</groupId>
|
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
|
||||||
<version>${mybatis-plus.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Knife4j (Swagger UI) -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.xiaoymin</groupId>
|
|
||||||
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
|
||||||
<version>${knife4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Lombok -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Validation -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Redis -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Apache Commons Pool2 (Redis连接池需要) -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-pool2</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 阿里云短信服务SDK -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.aliyun</groupId>
|
|
||||||
<artifactId>dysmsapi20170525</artifactId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 阿里云SDK依赖 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.aliyun</groupId>
|
|
||||||
<artifactId>tea-openapi</artifactId>
|
|
||||||
<version>0.3.9</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- fastjson (阿里云SDK依赖) -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba</groupId>
|
|
||||||
<artifactId>fastjson</artifactId>
|
|
||||||
<version>2.0.43</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<excludes>
|
|
||||||
<exclude>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
</exclude>
|
|
||||||
</excludes>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
package com.example.registration;
|
|
||||||
|
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
@MapperScan("com.example.registration.mapper")
|
|
||||||
public class RegistrationApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(RegistrationApplication.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
package com.example.registration.config;
|
|
||||||
|
|
||||||
import com.example.registration.util.Result;
|
|
||||||
import org.springframework.validation.BindException;
|
|
||||||
import org.springframework.validation.FieldError;
|
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|
||||||
|
|
||||||
import javax.validation.ConstraintViolation;
|
|
||||||
import javax.validation.ConstraintViolationException;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@RestControllerAdvice
|
|
||||||
public class GlobalExceptionHandler {
|
|
||||||
|
|
||||||
@ExceptionHandler(BindException.class)
|
|
||||||
public Result<Void> handleBindException(BindException e) {
|
|
||||||
String message = e.getFieldErrors().stream()
|
|
||||||
.map(FieldError::getDefaultMessage)
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
return Result.error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler(ConstraintViolationException.class)
|
|
||||||
public Result<Void> handleConstraintViolationException(ConstraintViolationException e) {
|
|
||||||
String message = e.getConstraintViolations().stream()
|
|
||||||
.map(ConstraintViolation::getMessage)
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
return Result.error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler(Exception.class)
|
|
||||||
public Result<Void> handleException(Exception e) {
|
|
||||||
return Result.error(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package com.example.registration.config;
|
|
||||||
|
|
||||||
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import springfox.documentation.builders.ApiInfoBuilder;
|
|
||||||
import springfox.documentation.builders.PathSelectors;
|
|
||||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
|
||||||
import springfox.documentation.service.ApiInfo;
|
|
||||||
import springfox.documentation.spi.DocumentationType;
|
|
||||||
import springfox.documentation.spring.web.plugins.Docket;
|
|
||||||
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* http://10.23.22.43:8099/doc.html
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@EnableSwagger2WebMvc
|
|
||||||
@EnableKnife4j
|
|
||||||
public class Knife4jConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Docket createRestApi() {
|
|
||||||
return new Docket(DocumentationType.SWAGGER_2)
|
|
||||||
.apiInfo(apiInfo())
|
|
||||||
.select()
|
|
||||||
.apis(RequestHandlerSelectors.basePackage("com.example.registration.controller"))
|
|
||||||
.paths(PathSelectors.any())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ApiInfo apiInfo() {
|
|
||||||
return new ApiInfoBuilder()
|
|
||||||
.title("培训报名系统 API")
|
|
||||||
.description("培训报名系统接口文档")
|
|
||||||
.version("1.0.0")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package com.example.registration.config;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class MybatisPlusConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
|
||||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
|
||||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
package com.example.registration.config;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class RedisConfig {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
|
||||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
|
||||||
template.setConnectionFactory(connectionFactory);
|
|
||||||
template.setKeySerializer(new StringRedisSerializer());
|
|
||||||
template.setValueSerializer(new StringRedisSerializer());
|
|
||||||
template.setHashKeySerializer(new StringRedisSerializer());
|
|
||||||
template.setHashValueSerializer(new StringRedisSerializer());
|
|
||||||
template.afterPropertiesSet();
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
package com.example.registration.config;
|
|
||||||
|
|
||||||
import com.example.registration.util.RedisTokenUtil;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class TokenInterceptor implements HandlerInterceptor {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisTokenUtil redisTokenUtil;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
|
||||||
// 放行 OPTIONS 预检请求
|
|
||||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
String token = request.getHeader("token");
|
|
||||||
if (token == null || token.isEmpty()) {
|
|
||||||
response.setContentType("application/json;charset=UTF-8");
|
|
||||||
response.setStatus(401);
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 401);
|
|
||||||
result.put("message", "token不能为空");
|
|
||||||
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!redisTokenUtil.validateToken(token)) {
|
|
||||||
response.setContentType("application/json;charset=UTF-8");
|
|
||||||
response.setStatus(401);
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
result.put("code", 401);
|
|
||||||
result.put("message", "token已过期或无效");
|
|
||||||
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 刷新token过期时间
|
|
||||||
redisTokenUtil.refreshToken(token);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
package com.example.registration.config;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class WebMvcConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TokenInterceptor tokenInterceptor;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
|
||||||
registry.addInterceptor(tokenInterceptor)
|
|
||||||
.addPathPatterns("/api/**")
|
|
||||||
.excludePathPatterns(
|
|
||||||
"/api/user/login",
|
|
||||||
"/api/registration/submit",
|
|
||||||
"/api/sms/send",
|
|
||||||
"/api/sms/verify"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
|
||||||
registry.addMapping("/**")
|
|
||||||
.allowedOriginPatterns("*")
|
|
||||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
|
||||||
.allowedHeaders("*")
|
|
||||||
.allowCredentials(true)
|
|
||||||
.maxAge(3600);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
package com.example.registration.controller;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.example.registration.dto.RegistrationRequest;
|
|
||||||
import com.example.registration.dto.RegistrationUpdateRequest;
|
|
||||||
import com.example.registration.dto.StatisticsResponse;
|
|
||||||
import com.example.registration.entity.TrainingRegistration;
|
|
||||||
import com.example.registration.service.RegistrationService;
|
|
||||||
import com.example.registration.service.SmsService;
|
|
||||||
import com.example.registration.util.Result;
|
|
||||||
import io.swagger.annotations.Api;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import io.swagger.annotations.ApiParam;
|
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/registration")
|
|
||||||
@Api(tags = "报名管理")
|
|
||||||
public class RegistrationController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RegistrationService registrationService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private SmsService smsService;
|
|
||||||
|
|
||||||
@PostMapping("/submit")
|
|
||||||
@ApiOperation("提交报名表单")
|
|
||||||
public Result<TrainingRegistration> submit(@RequestBody @Validated RegistrationRequest request) {
|
|
||||||
// 验证短信验证码
|
|
||||||
if (!smsService.verifyCode(request.getPhone(), request.getSmsCode())) {
|
|
||||||
return Result.error("短信验证码错误或已过期");
|
|
||||||
}
|
|
||||||
|
|
||||||
TrainingRegistration registration = new TrainingRegistration();
|
|
||||||
BeanUtils.copyProperties(request, registration);
|
|
||||||
TrainingRegistration result = registrationService.submit(registration);
|
|
||||||
return Result.success(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/list")
|
|
||||||
@ApiOperation("报名列表")
|
|
||||||
public Result<IPage<TrainingRegistration>> list(
|
|
||||||
@RequestParam(defaultValue = "1") @ApiParam("页码") Integer pageNum,
|
|
||||||
@RequestParam(defaultValue = "10") @ApiParam("每页条数") Integer pageSize,
|
|
||||||
@RequestParam(required = false) @ApiParam("关键词") String keyword,
|
|
||||||
@RequestParam(required = false) @ApiParam("状态") String status,
|
|
||||||
@RequestParam(required = false) @ApiParam("期次") String period) {
|
|
||||||
Page<TrainingRegistration> page = new Page<>(pageNum, pageSize);
|
|
||||||
return Result.success(registrationService.listPage(page, keyword, status, period));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("/update/{randomCode}")
|
|
||||||
@ApiOperation("修改报名信息")
|
|
||||||
public Result<TrainingRegistration> update(
|
|
||||||
@PathVariable @ApiParam("随机唯一编号") String randomCode,
|
|
||||||
@RequestBody RegistrationUpdateRequest request) {
|
|
||||||
TrainingRegistration registration = new TrainingRegistration();
|
|
||||||
BeanUtils.copyProperties(request, registration);
|
|
||||||
TrainingRegistration result = registrationService.updateByRandomCode(randomCode, registration);
|
|
||||||
if (result == null) {
|
|
||||||
return Result.error("报名信息不存在");
|
|
||||||
}
|
|
||||||
return Result.success(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/statistics")
|
|
||||||
@ApiOperation("统计报名信息")
|
|
||||||
public Result<StatisticsResponse> statistics() {
|
|
||||||
Map<String, Object> map = registrationService.statistics();
|
|
||||||
StatisticsResponse response = new StatisticsResponse();
|
|
||||||
response.setTotalRegistrations((Long) map.get("totalRegistrations"));
|
|
||||||
response.setPaidRegistrations((Long) map.get("paidRegistrations"));
|
|
||||||
response.setPendingPaymentRegistrations((Long) map.get("pendingPaymentRegistrations"));
|
|
||||||
response.setPaidAmount((java.math.BigDecimal) map.get("paidAmount"));
|
|
||||||
return Result.success(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package com.example.registration.controller;
|
|
||||||
|
|
||||||
import com.example.registration.dto.LoginRequest;
|
|
||||||
import com.example.registration.dto.LoginResponse;
|
|
||||||
import com.example.registration.entity.User;
|
|
||||||
import com.example.registration.service.UserService;
|
|
||||||
import com.example.registration.util.RedisTokenUtil;
|
|
||||||
import com.example.registration.util.Result;
|
|
||||||
import io.swagger.annotations.Api;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/user")
|
|
||||||
@Api(tags = "用户管理")
|
|
||||||
public class UserController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserService userService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisTokenUtil redisTokenUtil;
|
|
||||||
|
|
||||||
@PostMapping("/login")
|
|
||||||
@ApiOperation("用户登录")
|
|
||||||
public Result<LoginResponse> login(@RequestBody @Validated LoginRequest request) {
|
|
||||||
User user = userService.login(request.getUsername(), request.getPassword());
|
|
||||||
if (user == null) {
|
|
||||||
return Result.error("用户名或密码错误");
|
|
||||||
}
|
|
||||||
String token = UUID.randomUUID().toString().replace("-", "");
|
|
||||||
redisTokenUtil.storeToken(token, String.valueOf(user.getId()));
|
|
||||||
|
|
||||||
LoginResponse response = new LoginResponse();
|
|
||||||
response.setUserId(user.getId());
|
|
||||||
response.setUsername(user.getUsername());
|
|
||||||
response.setRealName(user.getRealName());
|
|
||||||
response.setRole(user.getRole());
|
|
||||||
response.setToken(token);
|
|
||||||
return Result.success(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/logout")
|
|
||||||
@ApiOperation("退出登录")
|
|
||||||
public Result<Void> logout(@RequestHeader("token") String token) {
|
|
||||||
redisTokenUtil.deleteToken(token);
|
|
||||||
return Result.success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package com.example.registration.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("登录请求")
|
|
||||||
public class LoginRequest {
|
|
||||||
|
|
||||||
@NotBlank(message = "用户名不能为空")
|
|
||||||
@ApiModelProperty("用户名")
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
@NotBlank(message = "密码不能为空")
|
|
||||||
@ApiModelProperty("密码")
|
|
||||||
private String password;
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
package com.example.registration.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("登录响应")
|
|
||||||
public class LoginResponse {
|
|
||||||
|
|
||||||
@ApiModelProperty("用户ID")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
@ApiModelProperty("用户名")
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
@ApiModelProperty("真实姓名")
|
|
||||||
private String realName;
|
|
||||||
|
|
||||||
@ApiModelProperty("角色")
|
|
||||||
private String role;
|
|
||||||
|
|
||||||
@ApiModelProperty("Token")
|
|
||||||
private String token;
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
package com.example.registration.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("提交报名表单请求")
|
|
||||||
public class RegistrationRequest {
|
|
||||||
|
|
||||||
@NotBlank(message = "姓名不能为空")
|
|
||||||
@ApiModelProperty("姓名(真实姓名)")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@NotBlank(message = "所在单位不能为空")
|
|
||||||
@ApiModelProperty("所在单位(学校/企业全称)")
|
|
||||||
private String company;
|
|
||||||
|
|
||||||
@ApiModelProperty("所在部门(院系/部门名称)")
|
|
||||||
private String department;
|
|
||||||
|
|
||||||
@NotBlank(message = "职务/职称不能为空")
|
|
||||||
@ApiModelProperty("职务/职称")
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@NotBlank(message = "手机号码不能为空")
|
|
||||||
@ApiModelProperty("手机号码")
|
|
||||||
private String phone;
|
|
||||||
|
|
||||||
@NotBlank(message = "短信验证码不能为空")
|
|
||||||
@ApiModelProperty("短信验证码")
|
|
||||||
private String smsCode;
|
|
||||||
|
|
||||||
@NotBlank(message = "电子邮箱不能为空")
|
|
||||||
@ApiModelProperty("电子邮箱")
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@NotBlank(message = "身份证号不能为空")
|
|
||||||
@ApiModelProperty("身份证号")
|
|
||||||
private String idCard;
|
|
||||||
|
|
||||||
@NotBlank(message = "报名期次不能为空")
|
|
||||||
@ApiModelProperty("报名期次")
|
|
||||||
private String period;
|
|
||||||
|
|
||||||
@NotNull(message = "培训费用不能为空")
|
|
||||||
@ApiModelProperty("培训费用(元)")
|
|
||||||
private BigDecimal fee;
|
|
||||||
|
|
||||||
@ApiModelProperty("备注说明(饮食禁忌、特殊需求等)")
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
@ApiModelProperty("推荐人")
|
|
||||||
private String referrer;
|
|
||||||
|
|
||||||
@ApiModelProperty("推荐人单位")
|
|
||||||
private String referrerCompany;
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
package com.example.registration.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("修改报名信息请求")
|
|
||||||
public class RegistrationUpdateRequest {
|
|
||||||
|
|
||||||
@ApiModelProperty("姓名(真实姓名)")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@ApiModelProperty("所在单位(学校/企业全称)")
|
|
||||||
private String company;
|
|
||||||
|
|
||||||
@ApiModelProperty("所在部门(院系/部门名称)")
|
|
||||||
private String department;
|
|
||||||
|
|
||||||
@ApiModelProperty("职务/职称")
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@ApiModelProperty("手机号码")
|
|
||||||
private String phone;
|
|
||||||
|
|
||||||
@ApiModelProperty("电子邮箱")
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@ApiModelProperty("身份证号")
|
|
||||||
private String idCard;
|
|
||||||
|
|
||||||
@ApiModelProperty("报名期次")
|
|
||||||
private String period;
|
|
||||||
|
|
||||||
@ApiModelProperty("培训费用(元)")
|
|
||||||
private BigDecimal fee;
|
|
||||||
|
|
||||||
@ApiModelProperty("备注说明(饮食禁忌、特殊需求等)")
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
@ApiModelProperty("状态")
|
|
||||||
private String status;
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package com.example.registration.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
|
||||||
import javax.validation.constraints.Pattern;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("发送短信请求")
|
|
||||||
public class SmsRequest {
|
|
||||||
|
|
||||||
@NotBlank(message = "手机号不能为空")
|
|
||||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
|
||||||
@ApiModelProperty("手机号码")
|
|
||||||
private String phone;
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
package com.example.registration.dto;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("统计信息响应")
|
|
||||||
public class StatisticsResponse {
|
|
||||||
|
|
||||||
@ApiModelProperty("报名人数")
|
|
||||||
private Long totalRegistrations;
|
|
||||||
|
|
||||||
@ApiModelProperty("已支付人数")
|
|
||||||
private Long paidRegistrations;
|
|
||||||
|
|
||||||
@ApiModelProperty("待支付人数")
|
|
||||||
private Long pendingPaymentRegistrations;
|
|
||||||
|
|
||||||
@ApiModelProperty("已支付金额")
|
|
||||||
private BigDecimal paidAmount;
|
|
||||||
}
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
package com.example.registration.entity;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@TableName("training_registration")
|
|
||||||
@ApiModel("培训报名表")
|
|
||||||
public class TrainingRegistration {
|
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
|
||||||
@ApiModelProperty("自增主键ID")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@ApiModelProperty("报名编号")
|
|
||||||
private String bmId;
|
|
||||||
|
|
||||||
@ApiModelProperty("随机唯一编号")
|
|
||||||
private String randomCode;
|
|
||||||
|
|
||||||
@ApiModelProperty("姓名")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@ApiModelProperty("所在单位")
|
|
||||||
private String company;
|
|
||||||
|
|
||||||
@ApiModelProperty("所在部门")
|
|
||||||
private String department;
|
|
||||||
|
|
||||||
@ApiModelProperty("职务/职称")
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@ApiModelProperty("手机号码")
|
|
||||||
private String phone;
|
|
||||||
|
|
||||||
@ApiModelProperty("电子邮箱")
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@ApiModelProperty("身份证号")
|
|
||||||
private String idCard;
|
|
||||||
|
|
||||||
@ApiModelProperty("报名期次")
|
|
||||||
private String period;
|
|
||||||
|
|
||||||
@ApiModelProperty("培训费用")
|
|
||||||
private BigDecimal fee;
|
|
||||||
|
|
||||||
@ApiModelProperty("备注说明")
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
@ApiModelProperty("短信验证码")
|
|
||||||
private String smsCode;
|
|
||||||
|
|
||||||
@ApiModelProperty("短信是否已验证")
|
|
||||||
private Boolean isVerified;
|
|
||||||
|
|
||||||
@ApiModelProperty("验证时间")
|
|
||||||
private LocalDateTime verificationTime;
|
|
||||||
|
|
||||||
@ApiModelProperty("状态")
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
@ApiModelProperty("提交时间")
|
|
||||||
private LocalDateTime submitTime;
|
|
||||||
|
|
||||||
@ApiModelProperty("创建时间")
|
|
||||||
private LocalDateTime createTime;
|
|
||||||
|
|
||||||
@ApiModelProperty("更新时间")
|
|
||||||
private LocalDateTime updateTime;
|
|
||||||
|
|
||||||
@ApiModelProperty("创建IP")
|
|
||||||
private String createIp;
|
|
||||||
|
|
||||||
@ApiModelProperty("创建用户代理")
|
|
||||||
private String createUserAgent;
|
|
||||||
|
|
||||||
@ApiModelProperty("推荐人")
|
|
||||||
private String referrer;
|
|
||||||
|
|
||||||
@ApiModelProperty("推荐人单位")
|
|
||||||
private String referrerCompany;
|
|
||||||
}
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
package com.example.registration.entity;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@TableName("user")
|
|
||||||
@ApiModel("用户")
|
|
||||||
public class User {
|
|
||||||
|
|
||||||
@TableId(type = IdType.AUTO)
|
|
||||||
@ApiModelProperty("用户ID")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@ApiModelProperty("用户名")
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
@ApiModelProperty("邮箱")
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@ApiModelProperty("手机号")
|
|
||||||
private String phone;
|
|
||||||
|
|
||||||
@ApiModelProperty("密码哈希值")
|
|
||||||
private String passwordHash;
|
|
||||||
|
|
||||||
@ApiModelProperty("密码盐值")
|
|
||||||
private String passwordSalt;
|
|
||||||
|
|
||||||
@ApiModelProperty("真实姓名")
|
|
||||||
private String realName;
|
|
||||||
|
|
||||||
@ApiModelProperty("昵称")
|
|
||||||
private String nickname;
|
|
||||||
|
|
||||||
@ApiModelProperty("头像URL")
|
|
||||||
private String avatar;
|
|
||||||
|
|
||||||
@ApiModelProperty("性别")
|
|
||||||
private String gender;
|
|
||||||
|
|
||||||
@ApiModelProperty("出生日期")
|
|
||||||
private LocalDate birthday;
|
|
||||||
|
|
||||||
@ApiModelProperty("账号状态")
|
|
||||||
private String status;
|
|
||||||
|
|
||||||
@ApiModelProperty("邮箱是否已验证")
|
|
||||||
private Boolean emailVerified;
|
|
||||||
|
|
||||||
@ApiModelProperty("手机号是否已验证")
|
|
||||||
private Boolean phoneVerified;
|
|
||||||
|
|
||||||
@ApiModelProperty("最后登录IP")
|
|
||||||
private String lastLoginIp;
|
|
||||||
|
|
||||||
@ApiModelProperty("最后登录时间")
|
|
||||||
private LocalDateTime lastLoginTime;
|
|
||||||
|
|
||||||
@ApiModelProperty("登录次数")
|
|
||||||
private Integer loginCount;
|
|
||||||
|
|
||||||
@ApiModelProperty("连续登录失败次数")
|
|
||||||
private Integer failedLoginAttempts;
|
|
||||||
|
|
||||||
@ApiModelProperty("账号锁定至")
|
|
||||||
private LocalDateTime accountLockedUntil;
|
|
||||||
|
|
||||||
@ApiModelProperty("密码最后修改时间")
|
|
||||||
private LocalDateTime passwordChangedAt;
|
|
||||||
|
|
||||||
@ApiModelProperty("密码过期时间")
|
|
||||||
private LocalDateTime passwordExpiresAt;
|
|
||||||
|
|
||||||
@ApiModelProperty("用户角色")
|
|
||||||
private String role;
|
|
||||||
|
|
||||||
@ApiModelProperty("创建时间")
|
|
||||||
private LocalDateTime createdAt;
|
|
||||||
|
|
||||||
@ApiModelProperty("更新时间")
|
|
||||||
private LocalDateTime updatedAt;
|
|
||||||
|
|
||||||
@ApiModelProperty("删除时间")
|
|
||||||
@TableLogic(value = "null", delval = "now()")
|
|
||||||
private LocalDateTime deletedAt;
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
package com.example.registration.mapper;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import com.example.registration.entity.TrainingRegistration;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface TrainingRegistrationMapper extends BaseMapper<TrainingRegistration> {
|
|
||||||
|
|
||||||
@Select("SELECT COUNT(*) FROM training_registration WHERE status != 'cancelled' AND status != 'draft'")
|
|
||||||
Long countTotalRegistrations();
|
|
||||||
|
|
||||||
@Select("SELECT COUNT(*) FROM training_registration WHERE status = 'paid'")
|
|
||||||
Long countPaidRegistrations();
|
|
||||||
|
|
||||||
@Select("SELECT COUNT(*) FROM training_registration WHERE status = 'submitted'")
|
|
||||||
Long countPendingPaymentRegistrations();
|
|
||||||
|
|
||||||
@Select("SELECT COALESCE(SUM(fee), 0) FROM training_registration WHERE status = 'paid'")
|
|
||||||
BigDecimal sumPaidAmount();
|
|
||||||
|
|
||||||
@Select("SELECT * FROM training_registration WHERE random_code = #{randomCode} LIMIT 1")
|
|
||||||
TrainingRegistration selectByRandomCode(@Param("randomCode") String randomCode);
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
package com.example.registration.mapper;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import com.example.registration.entity.User;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
|
|
||||||
@Mapper
|
|
||||||
public interface UserMapper extends BaseMapper<User> {
|
|
||||||
|
|
||||||
@Select("SELECT * FROM user WHERE username = #{username} LIMIT 1")
|
|
||||||
User selectByUsername(@Param("username") String username);
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package com.example.registration.service;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
|
||||||
import com.example.registration.entity.TrainingRegistration;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public interface RegistrationService extends IService<TrainingRegistration> {
|
|
||||||
|
|
||||||
TrainingRegistration submit(TrainingRegistration registration);
|
|
||||||
|
|
||||||
IPage<TrainingRegistration> listPage(Page<TrainingRegistration> page, String keyword, String status, String period);
|
|
||||||
|
|
||||||
TrainingRegistration updateByRandomCode(String randomCode, TrainingRegistration registration);
|
|
||||||
|
|
||||||
Map<String, Object> statistics();
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package com.example.registration.service;
|
|
||||||
|
|
||||||
public interface SmsService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送短信验证码
|
|
||||||
* @param phone 手机号
|
|
||||||
* @return 发送结果
|
|
||||||
*/
|
|
||||||
boolean sendCode(String phone);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证短信验证码
|
|
||||||
* @param phone 手机号
|
|
||||||
* @param code 验证码
|
|
||||||
* @return 验证结果
|
|
||||||
*/
|
|
||||||
boolean verifyCode(String phone, String code);
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
package com.example.registration.service;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
|
||||||
import com.example.registration.entity.User;
|
|
||||||
|
|
||||||
public interface UserService extends IService<User> {
|
|
||||||
|
|
||||||
User login(String username, String password);
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
package com.example.registration.service.impl;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
||||||
import com.example.registration.entity.TrainingRegistration;
|
|
||||||
import com.example.registration.mapper.TrainingRegistrationMapper;
|
|
||||||
import com.example.registration.service.RegistrationService;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class RegistrationServiceImpl extends ServiceImpl<TrainingRegistrationMapper, TrainingRegistration> implements RegistrationService {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TrainingRegistration submit(TrainingRegistration registration) {
|
|
||||||
// 生成报名编号 BMYYYYMMxxxx
|
|
||||||
String bmId = generateBmId();
|
|
||||||
registration.setBmId(bmId);
|
|
||||||
// 生成随机唯一编号
|
|
||||||
registration.setRandomCode(UUID.randomUUID().toString().replace("-", ""));
|
|
||||||
// 默认状态为已提交
|
|
||||||
if (StringUtils.isBlank(registration.getStatus())) {
|
|
||||||
registration.setStatus("submitted");
|
|
||||||
}
|
|
||||||
registration.setSubmitTime(LocalDateTime.now());
|
|
||||||
baseMapper.insert(registration);
|
|
||||||
return registration;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IPage<TrainingRegistration> listPage(Page<TrainingRegistration> page, String keyword, String status, String period) {
|
|
||||||
LambdaQueryWrapper<TrainingRegistration> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
if (StringUtils.isNotBlank(keyword)) {
|
|
||||||
wrapper.and(w -> w.like(TrainingRegistration::getName, keyword)
|
|
||||||
.or().like(TrainingRegistration::getPhone, keyword)
|
|
||||||
.or().like(TrainingRegistration::getCompany, keyword)
|
|
||||||
.or().like(TrainingRegistration::getBmId, keyword));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(status)) {
|
|
||||||
wrapper.eq(TrainingRegistration::getStatus, status);
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(period)) {
|
|
||||||
wrapper.eq(TrainingRegistration::getPeriod, period);
|
|
||||||
}
|
|
||||||
wrapper.orderByDesc(TrainingRegistration::getCreateTime);
|
|
||||||
return baseMapper.selectPage(page, wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TrainingRegistration updateByRandomCode(String randomCode, TrainingRegistration registration) {
|
|
||||||
TrainingRegistration existing = baseMapper.selectByRandomCode(randomCode);
|
|
||||||
if (existing == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
registration.setId(existing.getId());
|
|
||||||
registration.setBmId(null);
|
|
||||||
registration.setRandomCode(null);
|
|
||||||
registration.setCreateTime(null);
|
|
||||||
baseMapper.updateById(registration);
|
|
||||||
return baseMapper.selectById(existing.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> statistics() {
|
|
||||||
Map<String, Object> result = new HashMap<>();
|
|
||||||
Long total = baseMapper.countTotalRegistrations();
|
|
||||||
Long paid = baseMapper.countPaidRegistrations();
|
|
||||||
Long pending = baseMapper.countPendingPaymentRegistrations();
|
|
||||||
BigDecimal paidAmount = baseMapper.sumPaidAmount();
|
|
||||||
result.put("totalRegistrations", total);
|
|
||||||
result.put("paidRegistrations", paid);
|
|
||||||
result.put("pendingPaymentRegistrations", pending);
|
|
||||||
result.put("paidAmount", paidAmount);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateBmId() {
|
|
||||||
String prefix = "BM" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
|
|
||||||
// 查询当月最大编号
|
|
||||||
LambdaQueryWrapper<TrainingRegistration> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
wrapper.likeRight(TrainingRegistration::getBmId, prefix);
|
|
||||||
wrapper.orderByDesc(TrainingRegistration::getBmId);
|
|
||||||
wrapper.last("LIMIT 1");
|
|
||||||
TrainingRegistration last = baseMapper.selectOne(wrapper);
|
|
||||||
int seq = 1;
|
|
||||||
if (last != null && last.getBmId() != null) {
|
|
||||||
try {
|
|
||||||
String seqStr = last.getBmId().substring(prefix.length());
|
|
||||||
seq = Integer.parseInt(seqStr) + 1;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
seq = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prefix + String.format("%04d", seq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
package com.example.registration.service.impl;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
||||||
import com.example.registration.entity.User;
|
|
||||||
import com.example.registration.mapper.UserMapper;
|
|
||||||
import com.example.registration.service.UserService;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.DigestUtils;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User login(String username, String password) {
|
|
||||||
User user = baseMapper.selectByUsername(username);
|
|
||||||
if (user == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!"active".equals(user.getStatus())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String hashedPassword = DigestUtils.md5DigestAsHex(
|
|
||||||
(password + user.getPasswordSalt()).getBytes(StandardCharsets.UTF_8)
|
|
||||||
);
|
|
||||||
if (!hashedPassword.equals(user.getPasswordHash())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 更新登录信息
|
|
||||||
user.setLastLoginTime(LocalDateTime.now());
|
|
||||||
user.setLoginCount(user.getLoginCount() == null ? 1 : user.getLoginCount() + 1);
|
|
||||||
user.setFailedLoginAttempts(0);
|
|
||||||
baseMapper.updateById(user);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
package com.example.registration.util;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class RedisTokenUtil {
|
|
||||||
|
|
||||||
private static final String TOKEN_PREFIX = "token:";
|
|
||||||
private static final long TOKEN_EXPIRE_MINUTES = 30;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
|
||||||
|
|
||||||
public void storeToken(String token, String userId) {
|
|
||||||
String key = TOKEN_PREFIX + token;
|
|
||||||
redisTemplate.opsForValue().set(key, userId, TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean validateToken(String token) {
|
|
||||||
String key = TOKEN_PREFIX + token;
|
|
||||||
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserId(String token) {
|
|
||||||
String key = TOKEN_PREFIX + token;
|
|
||||||
Object value = redisTemplate.opsForValue().get(key);
|
|
||||||
return value != null ? value.toString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refreshToken(String token) {
|
|
||||||
String key = TOKEN_PREFIX + token;
|
|
||||||
if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
|
|
||||||
redisTemplate.expire(key, TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteToken(String token) {
|
|
||||||
String key = TOKEN_PREFIX + token;
|
|
||||||
redisTemplate.delete(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
package com.example.registration.util;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ApiModel("通用响应")
|
|
||||||
public class Result<T> {
|
|
||||||
|
|
||||||
@ApiModelProperty("状态码 200成功")
|
|
||||||
private Integer code;
|
|
||||||
|
|
||||||
@ApiModelProperty("提示信息")
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
@ApiModelProperty("响应数据")
|
|
||||||
private T data;
|
|
||||||
|
|
||||||
public static <T> Result<T> success(T data) {
|
|
||||||
Result<T> result = new Result<>();
|
|
||||||
result.setCode(200);
|
|
||||||
result.setMessage("success");
|
|
||||||
result.setData(data);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Result<T> success() {
|
|
||||||
return success(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Result<T> error(String message) {
|
|
||||||
Result<T> result = new Result<>();
|
|
||||||
result.setCode(500);
|
|
||||||
result.setMessage(message);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Result<T> error(Integer code, String message) {
|
|
||||||
Result<T> result = new Result<>();
|
|
||||||
result.setCode(code);
|
|
||||||
result.setMessage(message);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
server:
|
|
||||||
port: 8099
|
|
||||||
servlet:
|
|
||||||
context-path: /
|
|
||||||
|
|
||||||
spring:
|
|
||||||
application:
|
|
||||||
name: registration
|
|
||||||
mvc:
|
|
||||||
pathmatch:
|
|
||||||
matching-strategy: ant_path_matcher
|
|
||||||
datasource:
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
url: jdbc:mysql://ngsk.tech:3307/registration?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
username: root
|
|
||||||
password: ngsk0809
|
|
||||||
schema: classpath:data.sql
|
|
||||||
initialization-mode: always
|
|
||||||
redis:
|
|
||||||
host: ngsk.tech
|
|
||||||
port: 6389
|
|
||||||
password: ngsk0809
|
|
||||||
database: 0
|
|
||||||
lettuce:
|
|
||||||
pool:
|
|
||||||
max-active: 8
|
|
||||||
max-idle: 8
|
|
||||||
min-idle: 0
|
|
||||||
|
|
||||||
mybatis-plus:
|
|
||||||
mapper-locations: classpath:/mapper/**/*.xml
|
|
||||||
type-aliases-package: com.example.registration.entity
|
|
||||||
configuration:
|
|
||||||
map-underscore-to-camel-case: true
|
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
|
||||||
global-config:
|
|
||||||
db-config:
|
|
||||||
id-type: auto
|
|
||||||
logic-delete-field: deletedAt
|
|
||||||
logic-delete-value: "NOW()"
|
|
||||||
logic-not-delete-value: "NULL"
|
|
||||||
|
|
||||||
knife4j:
|
|
||||||
enable: true
|
|
||||||
setting:
|
|
||||||
language: zh_cn
|
|
||||||
|
|
||||||
# 阿里云短信配置
|
|
||||||
aliyun:
|
|
||||||
sms:
|
|
||||||
access-key-id: LTAI5t7peh76HpuVGhpXabPb
|
|
||||||
access-key-secret: Y119TqVZeaU7LcYRgbIHLpmscteQnw
|
|
||||||
sign-name: 培训报名成功模板
|
|
||||||
template-code: SMS_506365455
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
server:
|
|
||||||
port: 8099
|
|
||||||
servlet:
|
|
||||||
context-path: /
|
|
||||||
|
|
||||||
spring:
|
|
||||||
application:
|
|
||||||
name: registration
|
|
||||||
mvc:
|
|
||||||
pathmatch:
|
|
||||||
matching-strategy: ant_path_matcher
|
|
||||||
datasource:
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
url: jdbc:mysql://ngsk.tech:3307/registration?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
username: root
|
|
||||||
password: ngsk0809
|
|
||||||
schema: classpath:data.sql
|
|
||||||
initialization-mode: always
|
|
||||||
redis:
|
|
||||||
host: ngsk.tech
|
|
||||||
port: 6389
|
|
||||||
password: ngsk0809
|
|
||||||
database: 0
|
|
||||||
lettuce:
|
|
||||||
pool:
|
|
||||||
max-active: 8
|
|
||||||
max-idle: 8
|
|
||||||
min-idle: 0
|
|
||||||
|
|
||||||
mybatis-plus:
|
|
||||||
mapper-locations: classpath:/mapper/**/*.xml
|
|
||||||
type-aliases-package: com.example.registration.entity
|
|
||||||
configuration:
|
|
||||||
map-underscore-to-camel-case: true
|
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
|
||||||
global-config:
|
|
||||||
db-config:
|
|
||||||
id-type: auto
|
|
||||||
logic-delete-field: deletedAt
|
|
||||||
logic-delete-value: "NOW()"
|
|
||||||
logic-not-delete-value: "NULL"
|
|
||||||
|
|
||||||
knife4j:
|
|
||||||
enable: true
|
|
||||||
setting:
|
|
||||||
language: zh_cn
|
|
||||||
|
|
||||||
# 阿里云短信配置
|
|
||||||
aliyun:
|
|
||||||
sms:
|
|
||||||
access-key-id: LTAI5t7peh76HpuVGhpXabPb
|
|
||||||
access-key-secret: Y119TqVZeaU7LcYRgbIHLpmscteQnw
|
|
||||||
sign-name: 培训报名成功模板
|
|
||||||
template-code: SMS_506365455
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
server:
|
|
||||||
port: 8099
|
|
||||||
servlet:
|
|
||||||
context-path: /
|
|
||||||
|
|
||||||
spring:
|
|
||||||
application:
|
|
||||||
name: registration
|
|
||||||
mvc:
|
|
||||||
pathmatch:
|
|
||||||
matching-strategy: ant_path_matcher
|
|
||||||
datasource:
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
url: jdbc:mysql://ngsk.tech:3307/registration?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
username: root
|
|
||||||
password: ngsk0809
|
|
||||||
schema: classpath:data.sql
|
|
||||||
initialization-mode: always
|
|
||||||
redis:
|
|
||||||
host: ngsk.tech
|
|
||||||
port: 6389
|
|
||||||
password: ngsk0809
|
|
||||||
database: 0
|
|
||||||
lettuce:
|
|
||||||
pool:
|
|
||||||
max-active: 8
|
|
||||||
max-idle: 8
|
|
||||||
min-idle: 0
|
|
||||||
|
|
||||||
mybatis-plus:
|
|
||||||
mapper-locations: classpath:/mapper/**/*.xml
|
|
||||||
type-aliases-package: com.example.registration.entity
|
|
||||||
configuration:
|
|
||||||
map-underscore-to-camel-case: true
|
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
|
||||||
global-config:
|
|
||||||
db-config:
|
|
||||||
id-type: auto
|
|
||||||
logic-delete-field: deletedAt
|
|
||||||
logic-delete-value: "NOW()"
|
|
||||||
logic-not-delete-value: "NULL"
|
|
||||||
|
|
||||||
knife4j:
|
|
||||||
enable: true
|
|
||||||
setting:
|
|
||||||
language: zh_cn
|
|
||||||
|
|
||||||
# 阿里云短信配置
|
|
||||||
aliyun:
|
|
||||||
sms:
|
|
||||||
access-key-id: LTAI5t7peh76HpuVGhpXabPb
|
|
||||||
access-key-secret: Y119TqVZeaU7LcYRgbIHLpmscteQnw
|
|
||||||
sign-name: 培训报名成功模板
|
|
||||||
template-code: SMS_506365455
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
spring:
|
|
||||||
profiles:
|
|
||||||
active: pro
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<configuration>
|
|
||||||
<property name="LOG_PATH" value="logs"/>
|
|
||||||
<property name="LOG_FILE" value="registration"/>
|
|
||||||
|
|
||||||
<!-- 控制台输出 -->
|
|
||||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
<charset>UTF-8</charset>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 文件输出 -->
|
|
||||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${LOG_PATH}/${LOG_FILE}.log</file>
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
|
||||||
<charset>UTF-8</charset>
|
|
||||||
</encoder>
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
|
||||||
<fileNamePattern>${LOG_PATH}/${LOG_FILE}-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
|
||||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
|
||||||
<maxFileSize>100MB</maxFileSize>
|
|
||||||
</timeBasedFileNamingAndTriggeringPolicy>
|
|
||||||
<maxHistory>30</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 错误日志文件 -->
|
|
||||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${LOG_PATH}/${LOG_FILE}-error.log</file>
|
|
||||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
|
||||||
<level>ERROR</level>
|
|
||||||
<onMatch>ACCEPT</onMatch>
|
|
||||||
<onMismatch>DENY</onMismatch>
|
|
||||||
</filter>
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
|
||||||
<charset>UTF-8</charset>
|
|
||||||
</encoder>
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
|
||||||
<fileNamePattern>${LOG_PATH}/${LOG_FILE}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
|
||||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
|
||||||
<maxFileSize>50MB</maxFileSize>
|
|
||||||
</timeBasedFileNamingAndTriggeringPolicy>
|
|
||||||
<maxHistory>60</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- 开发环境配置 -->
|
|
||||||
<springProfile name="dev">
|
|
||||||
<root level="DEBUG">
|
|
||||||
<appender-ref ref="CONSOLE"/>
|
|
||||||
<appender-ref ref="FILE"/>
|
|
||||||
<appender-ref ref="ERROR_FILE"/>
|
|
||||||
</root>
|
|
||||||
<logger name="com.example.registration" level="DEBUG"/>
|
|
||||||
</springProfile>
|
|
||||||
|
|
||||||
<!-- 测试环境配置 -->
|
|
||||||
<springProfile name="test">
|
|
||||||
<root level="INFO">
|
|
||||||
<appender-ref ref="CONSOLE"/>
|
|
||||||
<appender-ref ref="FILE"/>
|
|
||||||
<appender-ref ref="ERROR_FILE"/>
|
|
||||||
</root>
|
|
||||||
<logger name="com.example.registration" level="INFO"/>
|
|
||||||
</springProfile>
|
|
||||||
|
|
||||||
<!-- 生产环境配置 -->
|
|
||||||
<springProfile name="prod">
|
|
||||||
<root level="WARN">
|
|
||||||
<appender-ref ref="CONSOLE"/>
|
|
||||||
<appender-ref ref="FILE"/>
|
|
||||||
<appender-ref ref="ERROR_FILE"/>
|
|
||||||
</root>
|
|
||||||
<logger name="com.example.registration" level="INFO"/>
|
|
||||||
</springProfile>
|
|
||||||
|
|
||||||
</configuration>
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<?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.example.registration.mapper.TrainingRegistrationMapper">
|
|
||||||
</mapper>
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<?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.example.registration.mapper.UserMapper">
|
|
||||||
</mapper>
|
|
||||||
Loading…
Reference in New Issue