first commit
This commit is contained in:
96
pom.xml
Normal file
96
pom.xml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?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.15</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.discipline</groupId>
|
||||||
|
<artifactId>report</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<name>discipline-report</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Boot核心依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySQL驱动 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 二维码生成ZXing -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.zxing</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.zxing</groupId>
|
||||||
|
<artifactId>javase</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 测试依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</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>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.discipline.report;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
|
||||||
|
import com.discipline.report.config.CustomConfigProperties;
|
||||||
|
import com.discipline.report.config.FileUploadProperties;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@MapperScan("com.discipline.report.mapper")
|
||||||
|
@EnableConfigurationProperties({CustomConfigProperties.class, FileUploadProperties.class})
|
||||||
|
public class DisciplineReportApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DisciplineReportApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.discipline.report.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义配置属性
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "custom")
|
||||||
|
public class CustomConfigProperties {
|
||||||
|
private String disciplineEmail; // 纪委接收邮箱
|
||||||
|
private String h5IndexUrl; // H5首页URL
|
||||||
|
private String tipContent; // 填报提示文案
|
||||||
|
private String qrCodeFixedUrl; // 二维码固定URL
|
||||||
|
private String emailFrom; // 发件人邮箱
|
||||||
|
|
||||||
|
private String emailSubject;
|
||||||
|
private String emailContent;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.discipline.report.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传配置属性
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "discipline.file.upload")
|
||||||
|
public class FileUploadProperties {
|
||||||
|
private String path; // 文件上传路径
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(Paths.get(path));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("文件目录创建失败:{}", path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/main/java/com/discipline/report/config/WebMvcConfig.java
Normal file
21
src/main/java/com/discipline/report/config/WebMvcConfig.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.discipline.report.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web MVC配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedOriginPatterns("*")
|
||||||
|
.allowedMethods("GET", "POST", "PUT", "DELETE")
|
||||||
|
.allowedHeaders("*")
|
||||||
|
.allowCredentials(true)
|
||||||
|
.maxAge(3600);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.discipline.report.controller;
|
||||||
|
|
||||||
|
import com.discipline.report.config.CustomConfigProperties;
|
||||||
|
import com.discipline.report.result.Result;
|
||||||
|
import com.discipline.report.result.ResultUtil;
|
||||||
|
import com.discipline.report.util.QrCodeUtil;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码生成控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/discipline/qrcode")
|
||||||
|
public class QrCodeController {
|
||||||
|
@Resource
|
||||||
|
private CustomConfigProperties config;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public Result<?> getQrCode() {
|
||||||
|
try {
|
||||||
|
String fixedUrl = config.getQrCodeFixedUrl();
|
||||||
|
if (fixedUrl == null || fixedUrl.trim().isEmpty()) {
|
||||||
|
return ResultUtil.error(500, "配置文件未配置二维码固定链接");
|
||||||
|
}
|
||||||
|
String qrCode = QrCodeUtil.createQrCode(fixedUrl);
|
||||||
|
return ResultUtil.success(qrCode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResultUtil.error(500, "生成二维码失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.discipline.report.controller;
|
||||||
|
import com.discipline.report.dto.ReportSubmitDTO;
|
||||||
|
import com.discipline.report.result.Result;
|
||||||
|
import com.discipline.report.result.ResultUtil;
|
||||||
|
import com.discipline.report.service.ReportService;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
/**
|
||||||
|
* 举报提交控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/discipline/report")
|
||||||
|
public class ReportController {
|
||||||
|
@Resource
|
||||||
|
private ReportService reportService;
|
||||||
|
/**
|
||||||
|
* 提交举报表单+附件
|
||||||
|
*/
|
||||||
|
@PostMapping("/submit")
|
||||||
|
public Result submit(@Valid ReportSubmitDTO dto) {
|
||||||
|
try {
|
||||||
|
reportService.saveReportAndSendEmail(dto);
|
||||||
|
return ResultUtil.success("举报提交成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 移除e.printStackTrace(),全局异常统一打印日志
|
||||||
|
return ResultUtil.error(500, "系统异常,提交失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main/java/com/discipline/report/dto/ReportSubmitDTO.java
Normal file
29
src/main/java/com/discipline/report/dto/ReportSubmitDTO.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package com.discipline.report.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报提交数据传输对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReportSubmitDTO {
|
||||||
|
@NotNull(message = "是否匿名不能为空")
|
||||||
|
private Boolean isAnonymous; // 是否匿名
|
||||||
|
private String reporterName; // 举报人姓名
|
||||||
|
@NotBlank(message = "举报内容不能为空")
|
||||||
|
private String reportContent; // 举报内容
|
||||||
|
private String contactInfo; // 联系方式
|
||||||
|
private MultipartFile[] file; // 上传文件数组
|
||||||
|
|
||||||
|
public MultipartFile[] getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFile(MultipartFile[] file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.discipline.report.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报邮件接收人配置实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReportEmailReceiver {
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收人姓名
|
||||||
|
*/
|
||||||
|
private String receiverName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收人邮箱地址
|
||||||
|
*/
|
||||||
|
private String receiverEmail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收类型:1-主送(TO) 2-抄送(CC)
|
||||||
|
*/
|
||||||
|
private Integer receiveType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用 1-启用
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
18
src/main/java/com/discipline/report/entity/ReportFile.java
Normal file
18
src/main/java/com/discipline/report/entity/ReportFile.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package com.discipline.report.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报文件实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReportFile {
|
||||||
|
private Long id; // 主键ID
|
||||||
|
private Long reportId; // 举报记录ID
|
||||||
|
private String fileName; // 文件名
|
||||||
|
private String filePath; // 文件路径
|
||||||
|
private Long fileSize; // 文件大小
|
||||||
|
private LocalDateTime createTime;// 创建时间
|
||||||
|
}
|
||||||
19
src/main/java/com/discipline/report/entity/ReportRecord.java
Normal file
19
src/main/java/com/discipline/report/entity/ReportRecord.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package com.discipline.report.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报记录实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReportRecord {
|
||||||
|
private Long id; // 主键ID
|
||||||
|
private Boolean isAnonymous; // 是否匿名
|
||||||
|
private String reporterName; // 举报人姓名
|
||||||
|
private String reportContent; // 举报内容
|
||||||
|
private String contactInfo; // 联系方式
|
||||||
|
private String emailStatus; // 邮件发送状态
|
||||||
|
private LocalDateTime createTime;// 创建时间
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.discipline.report.exception;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常类
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class BusinessException extends RuntimeException {
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
public BusinessException(int code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.discipline.report.exception;
|
||||||
|
|
||||||
|
import com.discipline.report.result.Result;
|
||||||
|
import com.discipline.report.result.ResultUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
@ExceptionHandler(BusinessException.class)
|
||||||
|
@ResponseBody
|
||||||
|
public Result<?> businessException(BusinessException e) {
|
||||||
|
log.error("业务异常:{}", e.getMessage());
|
||||||
|
return ResultUtil.error(e.getCode(), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
@ResponseBody
|
||||||
|
public Result<?> validException(MethodArgumentNotValidException e) {
|
||||||
|
String msg = e.getBindingResult().getFieldError().getDefaultMessage();
|
||||||
|
log.error("参数校验异常:{}", msg);
|
||||||
|
return ResultUtil.error(400, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
@ResponseBody
|
||||||
|
public Result<?> exception(Exception e) {
|
||||||
|
log.error("系统异常", e);
|
||||||
|
return ResultUtil.error(500, "系统异常,请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.discipline.report.mapper;
|
||||||
|
|
||||||
|
import com.discipline.report.entity.ReportEmailReceiver;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报邮件接收人Mapper
|
||||||
|
*/
|
||||||
|
public interface ReportEmailReceiverMapper {
|
||||||
|
/**
|
||||||
|
* 根据接收类型查询启用的接收人列表
|
||||||
|
* @param receiveType 接收类型 1-主送 2-抄送
|
||||||
|
* @return 接收人列表
|
||||||
|
*/
|
||||||
|
List<ReportEmailReceiver> selectByReceiveType(@Param("receiveType") Integer receiveType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有启用的接收人
|
||||||
|
* @return 接收人列表
|
||||||
|
*/
|
||||||
|
List<ReportEmailReceiver> selectAllEnabled();
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.discipline.report.mapper;
|
||||||
|
|
||||||
|
import com.discipline.report.entity.ReportFile;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报文件数据访问接口
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ReportFileMapper {
|
||||||
|
void insertBatch(@Param("files") List<ReportFile> files);
|
||||||
|
List<ReportFile> selectByReportId(@Param("reportId") Long reportId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.discipline.report.mapper;
|
||||||
|
|
||||||
|
import com.discipline.report.entity.ReportRecord;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报记录数据访问接口
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ReportRecordMapper {
|
||||||
|
void insert(ReportRecord record);
|
||||||
|
// 新增:根据主键更新邮件状态
|
||||||
|
int updateById(ReportRecord record);
|
||||||
|
}
|
||||||
13
src/main/java/com/discipline/report/result/Result.java
Normal file
13
src/main/java/com/discipline/report/result/Result.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.discipline.report.result;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一返回结果类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Result<T> {
|
||||||
|
private int code; // 响应码
|
||||||
|
private String msg; // 响应消息
|
||||||
|
private T data; // 响应数据
|
||||||
|
}
|
||||||
37
src/main/java/com/discipline/report/result/ResultUtil.java
Normal file
37
src/main/java/com/discipline/report/result/ResultUtil.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package com.discipline.report.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一返回结果工具类
|
||||||
|
*/
|
||||||
|
public class ResultUtil {
|
||||||
|
/**
|
||||||
|
* 成功返回(带数据)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success(T data) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMsg("成功");
|
||||||
|
result.setData(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功返回(无数据)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success() {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMsg("成功");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败返回
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(int code, String msg) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(code);
|
||||||
|
result.setMsg(msg);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.discipline.report.service;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传服务接口
|
||||||
|
*/
|
||||||
|
public interface FileUploadService {
|
||||||
|
/**
|
||||||
|
* 上传单个文件,返回本地全路径
|
||||||
|
* @param file 前端上传文件
|
||||||
|
* @return 本地磁盘全路径
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
String uploadFile(MultipartFile file) throws IOException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.discipline.report.service;
|
||||||
|
|
||||||
|
import com.discipline.report.entity.ReportEmailReceiver;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报邮件接收人服务接口
|
||||||
|
*/
|
||||||
|
public interface ReportEmailReceiverService {
|
||||||
|
/**
|
||||||
|
* 获取主送接收人邮箱列表
|
||||||
|
* @return 邮箱数组
|
||||||
|
*/
|
||||||
|
String[] getToEmailArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取抄送接收人邮箱列表
|
||||||
|
* @return 邮箱数组
|
||||||
|
*/
|
||||||
|
String[] getCcEmailArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有启用的接收人
|
||||||
|
* @return 接收人列表
|
||||||
|
*/
|
||||||
|
List<ReportEmailReceiver> getAllEnabledReceivers();
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.discipline.report.service;
|
||||||
|
|
||||||
|
import com.discipline.report.dto.ReportSubmitDTO;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报服务接口
|
||||||
|
*/
|
||||||
|
public interface ReportService {
|
||||||
|
void saveReportAndSendEmail(ReportSubmitDTO dto) throws Exception;
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.discipline.report.service.impl;
|
||||||
|
|
||||||
|
import com.discipline.report.config.FileUploadProperties;
|
||||||
|
import com.discipline.report.exception.BusinessException;
|
||||||
|
import com.discipline.report.service.FileUploadService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传服务实现
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class FileUploadServiceImpl implements FileUploadService {
|
||||||
|
// 修正注释:原注释写的10MB但值是50MB,保持值不变仅修正注释(可选)
|
||||||
|
private static final long MAX_SIZE = 50*1024*1024; // 50MB
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FileUploadProperties uploadProperties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String uploadFile(MultipartFile file) throws IOException {
|
||||||
|
// 1. 校验文件是否为空
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
throw new BusinessException(400, "上传文件不能为空");
|
||||||
|
}
|
||||||
|
// 2. 校验文件大小
|
||||||
|
if (file.getSize() > MAX_SIZE) {
|
||||||
|
throw new BusinessException(400, "文件大小不能超过50MB");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 校验文件名是否为空(仅校验名称,不再校验后缀)
|
||||||
|
String originalName = file.getOriginalFilename();
|
||||||
|
if (originalName == null || originalName.trim().isEmpty()) {
|
||||||
|
throw new BusinessException(400, "上传文件名称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 生成新文件名(兼容无后缀文件)
|
||||||
|
String newFileName;
|
||||||
|
int dotIndex = originalName.lastIndexOf(".");
|
||||||
|
if (dotIndex == -1) {
|
||||||
|
// 无后缀文件,直接用UUID作为文件名
|
||||||
|
newFileName = UUID.randomUUID().toString();
|
||||||
|
} else {
|
||||||
|
// 有后缀文件,UUID + 原后缀
|
||||||
|
String suffix = originalName.substring(dotIndex).toLowerCase();
|
||||||
|
newFileName = UUID.randomUUID().toString() + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 保存文件
|
||||||
|
String uploadPath = uploadProperties.getPath();
|
||||||
|
File dest = new File(uploadPath + File.separator + newFileName);
|
||||||
|
// 确保上传目录存在(新增:防止目录不存在导致报错)
|
||||||
|
if (!dest.getParentFile().exists()) {
|
||||||
|
boolean mkdirs = dest.getParentFile().mkdirs();
|
||||||
|
if (!mkdirs) {
|
||||||
|
log.error("创建上传目录失败:{}", dest.getParentFile().getAbsolutePath());
|
||||||
|
throw new BusinessException(500, "文件上传失败,创建目录失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.transferTo(dest);
|
||||||
|
log.info("文件上传成功:{}", dest.getAbsolutePath());
|
||||||
|
|
||||||
|
return dest.getAbsolutePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.discipline.report.service.impl;
|
||||||
|
|
||||||
|
import com.discipline.report.entity.ReportEmailReceiver;
|
||||||
|
import com.discipline.report.mapper.ReportEmailReceiverMapper;
|
||||||
|
import com.discipline.report.service.ReportEmailReceiverService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报邮件接收人服务实现
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class ReportEmailReceiverServiceImpl implements ReportEmailReceiverService {
|
||||||
|
@Resource
|
||||||
|
private ReportEmailReceiverMapper receiverMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getToEmailArray() {
|
||||||
|
List<ReportEmailReceiver> toList = receiverMapper.selectByReceiveType(1);
|
||||||
|
return toList.stream()
|
||||||
|
.map(ReportEmailReceiver::getReceiverEmail)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCcEmailArray() {
|
||||||
|
List<ReportEmailReceiver> ccList = receiverMapper.selectByReceiveType(2);
|
||||||
|
return ccList.stream()
|
||||||
|
.map(ReportEmailReceiver::getReceiverEmail)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReportEmailReceiver> getAllEnabledReceivers() {
|
||||||
|
return receiverMapper.selectAllEnabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package com.discipline.report.service.impl;
|
||||||
|
import com.discipline.report.config.CustomConfigProperties;
|
||||||
|
import com.discipline.report.dto.ReportSubmitDTO;
|
||||||
|
import com.discipline.report.entity.ReportFile;
|
||||||
|
import com.discipline.report.entity.ReportRecord;
|
||||||
|
import com.discipline.report.mapper.ReportFileMapper;
|
||||||
|
import com.discipline.report.mapper.ReportRecordMapper;
|
||||||
|
import com.discipline.report.service.FileUploadService;
|
||||||
|
import com.discipline.report.service.ReportEmailReceiverService;
|
||||||
|
import com.discipline.report.service.ReportService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 举报服务实现类
|
||||||
|
* 负责举报记录保存、文件上传、举报通知邮件发送
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class ReportServiceImpl implements ReportService {
|
||||||
|
/**
|
||||||
|
* 邮件发送状态枚举
|
||||||
|
*/
|
||||||
|
private enum EmailStatus {
|
||||||
|
PENDING("PENDING"), // 待发送
|
||||||
|
SUCCESS("SUCCESS"), // 发送成功
|
||||||
|
FAIL("FAIL"); // 发送失败
|
||||||
|
private final String value;
|
||||||
|
EmailStatus(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ReportRecordMapper reportRecordMapper;
|
||||||
|
@Resource
|
||||||
|
private ReportFileMapper reportFileMapper;
|
||||||
|
@Resource
|
||||||
|
private FileUploadService fileUploadService;
|
||||||
|
@Resource
|
||||||
|
private JavaMailSender mailSender;
|
||||||
|
@Resource
|
||||||
|
private ReportEmailReceiverService receiverService;
|
||||||
|
@Resource
|
||||||
|
private CustomConfigProperties config;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void saveReportAndSendEmail(ReportSubmitDTO dto) throws Exception {
|
||||||
|
// 1. 保存举报主记录
|
||||||
|
ReportRecord reportRecord = saveReportRecord(dto);
|
||||||
|
// 2. 处理举报附件并保存
|
||||||
|
handleReportFiles(reportRecord.getId(), dto.getFile());
|
||||||
|
// 3. 发送举报通知邮件
|
||||||
|
sendReportNotificationEmail(reportRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存举报主记录
|
||||||
|
*/
|
||||||
|
private ReportRecord saveReportRecord(ReportSubmitDTO dto) {
|
||||||
|
ReportRecord record = new ReportRecord();
|
||||||
|
record.setIsAnonymous(dto.getIsAnonymous());
|
||||||
|
// 优化:匿名自动清空姓名和联系方式
|
||||||
|
if(Boolean.TRUE.equals(dto.getIsAnonymous())){
|
||||||
|
record.setReporterName(null);
|
||||||
|
record.setContactInfo(null);
|
||||||
|
}else{
|
||||||
|
record.setReporterName(dto.getReporterName());
|
||||||
|
record.setContactInfo(dto.getContactInfo());
|
||||||
|
}
|
||||||
|
record.setReportContent(dto.getReportContent());
|
||||||
|
record.setEmailStatus(EmailStatus.PENDING.getValue());
|
||||||
|
record.setCreateTime(LocalDateTime.now());
|
||||||
|
reportRecordMapper.insert(record);
|
||||||
|
log.info("举报记录保存成功,举报ID:{}", record.getId());
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理举报附件上传并保存
|
||||||
|
*/
|
||||||
|
private void handleReportFiles(Long reportId, MultipartFile[] files) {
|
||||||
|
if (files == null || files.length == 0) {
|
||||||
|
log.info("举报ID:{} 无附件需要上传", reportId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<ReportFile> reportFiles = new ArrayList<>();
|
||||||
|
for (MultipartFile file : files) {
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
log.warn("举报ID:{} 存在空文件,跳过处理", reportId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String filePath = fileUploadService.uploadFile(file);
|
||||||
|
ReportFile reportFile = new ReportFile();
|
||||||
|
reportFile.setReportId(reportId);
|
||||||
|
reportFile.setFileName(file.getOriginalFilename());
|
||||||
|
reportFile.setFilePath(filePath);
|
||||||
|
reportFile.setFileSize(file.getSize());
|
||||||
|
reportFile.setCreateTime(LocalDateTime.now());
|
||||||
|
reportFiles.add(reportFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("举报ID:{} 附件上传失败,文件名:{}", reportId, file.getOriginalFilename(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!reportFiles.isEmpty()) {
|
||||||
|
reportFileMapper.insertBatch(reportFiles);
|
||||||
|
log.info("举报ID:{} 成功保存{}个附件记录", reportId, reportFiles.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送举报通知邮件(优化:添加附件 + 拼接举报详情正文)
|
||||||
|
*/
|
||||||
|
private void sendReportNotificationEmail(ReportRecord reportRecord) {
|
||||||
|
Long reportId = reportRecord.getId();
|
||||||
|
try {
|
||||||
|
MimeMessage mimeMessage = mailSender.createMimeMessage();
|
||||||
|
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
|
||||||
|
// 设置邮件基础信息
|
||||||
|
helper.setFrom(config.getEmailFrom());
|
||||||
|
String subject = config.getEmailSubject() != null ? config.getEmailSubject() : "新的举报通知";
|
||||||
|
helper.setSubject(subject);
|
||||||
|
|
||||||
|
// ========== 核心:拼接举报详情到邮件正文 ==========
|
||||||
|
String baseContent = config.getEmailContent() != null ? config.getEmailContent() : "<p>收到一条新的举报信息,请及时处理!</p>";
|
||||||
|
StringBuilder sb = new StringBuilder(baseContent);
|
||||||
|
sb.append("<hr>");
|
||||||
|
sb.append("<p><strong>是否匿名:</strong>").append(reportRecord.getIsAnonymous() ? "是" : "否").append("</p>");
|
||||||
|
if (!reportRecord.getIsAnonymous()) {
|
||||||
|
sb.append("<p><strong>举报人:</strong>").append(reportRecord.getReporterName()).append("</p>");
|
||||||
|
sb.append("<p><strong>联系方式:</strong>").append(reportRecord.getContactInfo()).append("</p>");
|
||||||
|
}
|
||||||
|
sb.append("<p><strong>举报内容:</strong>").append(reportRecord.getReportContent()).append("</p>");
|
||||||
|
helper.setText(sb.toString(), true);
|
||||||
|
// ================================================
|
||||||
|
|
||||||
|
// 获取主送、抄送邮箱
|
||||||
|
String[] toEmails = receiverService.getToEmailArray();
|
||||||
|
String[] ccEmails = receiverService.getCcEmailArray();
|
||||||
|
|
||||||
|
// 主送为空直接失败
|
||||||
|
if (toEmails == null || toEmails.length == 0) {
|
||||||
|
log.warn("举报ID:{} 邮件主送人为空,无法发送邮件", reportId);
|
||||||
|
updateEmailStatus(reportRecord, EmailStatus.FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
helper.setTo(toEmails);
|
||||||
|
if (ccEmails != null && ccEmails.length > 0) {
|
||||||
|
helper.setCc(ccEmails);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询附件并添加到邮件
|
||||||
|
List<ReportFile> fileList = reportFileMapper.selectByReportId(reportId);
|
||||||
|
for(ReportFile rf : fileList){
|
||||||
|
File attachFile = new File(rf.getFilePath());
|
||||||
|
if(attachFile.exists()){
|
||||||
|
helper.addAttachment(rf.getFileName(), new FileSystemResource(attachFile));
|
||||||
|
}else{
|
||||||
|
log.warn("附件文件不存在,路径:{}",rf.getFilePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送邮件
|
||||||
|
mailSender.send(mimeMessage);
|
||||||
|
updateEmailStatus(reportRecord, EmailStatus.SUCCESS);
|
||||||
|
log.info("举报邮件发送成功,举报ID:{},主送:{},抄送:{}",
|
||||||
|
reportId,
|
||||||
|
arrayToString(toEmails),
|
||||||
|
arrayToString(ccEmails));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("举报邮件发送失败,举报ID:{}", reportId, e);
|
||||||
|
updateEmailStatus(reportRecord, EmailStatus.FAIL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新邮件状态
|
||||||
|
*/
|
||||||
|
private void updateEmailStatus(ReportRecord reportRecord, EmailStatus status) {
|
||||||
|
reportRecord.setEmailStatus(status.getValue());
|
||||||
|
reportRecordMapper.updateById(reportRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组转字符串(空安全)
|
||||||
|
*/
|
||||||
|
private String arrayToString(String[] array) {
|
||||||
|
if (array == null || array.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return String.join(",", array);
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main/java/com/discipline/report/util/EmailSendUtil.java
Normal file
62
src/main/java/com/discipline/report/util/EmailSendUtil.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package com.discipline.report.util;
|
||||||
|
|
||||||
|
import com.discipline.report.config.CustomConfigProperties;
|
||||||
|
import com.discipline.report.entity.ReportFile;
|
||||||
|
import com.discipline.report.entity.ReportRecord;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件发送工具类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class EmailSendUtil {
|
||||||
|
@Resource
|
||||||
|
private JavaMailSender mailSender;
|
||||||
|
@Resource
|
||||||
|
private CustomConfigProperties config;
|
||||||
|
|
||||||
|
public void sendReportEmail(ReportRecord record, List<ReportFile> fileList) throws MessagingException {
|
||||||
|
MimeMessage message = mailSender.createMimeMessage();
|
||||||
|
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
|
||||||
|
|
||||||
|
helper.setFrom(config.getEmailFrom());
|
||||||
|
helper.setTo(config.getDisciplineEmail());
|
||||||
|
helper.setSubject("【纪律举报】新举报信息提交");
|
||||||
|
|
||||||
|
// 构建邮件内容
|
||||||
|
StringBuilder content = new StringBuilder();
|
||||||
|
content.append("<h3>新举报信息</h3>");
|
||||||
|
content.append("<p><strong>举报时间:</strong>").append(record.getCreateTime()).append("</p>");
|
||||||
|
content.append("<p><strong>是否匿名:</strong>").append(record.getIsAnonymous() ? "是" : "否").append("</p>");
|
||||||
|
if (!record.getIsAnonymous() && record.getReporterName() != null) {
|
||||||
|
content.append("<p><strong>举报人:</strong>").append(record.getReporterName()).append("</p>");
|
||||||
|
}
|
||||||
|
if (record.getContactInfo() != null) {
|
||||||
|
content.append("<p><strong>联系方式:</strong>").append(record.getContactInfo()).append("</p>");
|
||||||
|
}
|
||||||
|
content.append("<p><strong>举报内容:</strong><br/>").append(record.getReportContent().replace("\n", "<br/>")).append("</p>");
|
||||||
|
|
||||||
|
// 添加附件
|
||||||
|
if (fileList != null && !fileList.isEmpty()) {
|
||||||
|
content.append("<p><strong>附件列表:</strong></p>");
|
||||||
|
for (ReportFile file : fileList) {
|
||||||
|
content.append("<p>").append(file.getFileName()).append(" (").append(file.getFileSize()/1024).append("KB)").append("</p>");
|
||||||
|
helper.addAttachment(file.getFileName(), new File(file.getFilePath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.setText(content.toString(), true);
|
||||||
|
mailSender.send(message);
|
||||||
|
log.info("举报邮件发送成功,收件人:{}", config.getDisciplineEmail());
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/main/java/com/discipline/report/util/QrCodeUtil.java
Normal file
67
src/main/java/com/discipline/report/util/QrCodeUtil.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package com.discipline.report.util;
|
||||||
|
|
||||||
|
import com.google.zxing.*;
|
||||||
|
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.qrcode.QRCodeWriter;
|
||||||
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码生成工具类(基于ZXing实现)
|
||||||
|
* 用于生成固定尺寸、固定纠错级别的Base64格式二维码
|
||||||
|
*/
|
||||||
|
public class QrCodeUtil {
|
||||||
|
// 固定配置:确保每次生成的二维码完全一致
|
||||||
|
private static final int QR_CODE_SIZE = 300; // 二维码尺寸(300x300像素)
|
||||||
|
private static final ErrorCorrectionLevel ERROR_CORRECTION = ErrorCorrectionLevel.H; // 高纠错级别(30%容错)
|
||||||
|
private static final String IMAGE_FORMAT = "png"; // 图片格式
|
||||||
|
private static final String CHARSET = "UTF-8"; // 字符编码
|
||||||
|
private static final int MARGIN = 1; // 二维码边距(最小)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成Base64格式的二维码图片
|
||||||
|
* @param content 二维码内容(前端提供的固定URL)
|
||||||
|
* @return Base64编码的图片字符串(带data:image/png;base64前缀)
|
||||||
|
* @throws Exception 生成过程中的异常
|
||||||
|
*/
|
||||||
|
public static String createQrCode(String content) throws Exception {
|
||||||
|
// 校验输入内容
|
||||||
|
if (content == null || content.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("二维码链接不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
QRCodeWriter writer = new QRCodeWriter();
|
||||||
|
|
||||||
|
// 配置二维码参数(固定配置,确保结果一致)
|
||||||
|
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||||
|
hints.put(EncodeHintType.ERROR_CORRECTION, ERROR_CORRECTION);
|
||||||
|
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
|
||||||
|
hints.put(EncodeHintType.MARGIN, MARGIN);
|
||||||
|
|
||||||
|
// 生成固定尺寸的二维码矩阵
|
||||||
|
BitMatrix matrix = writer.encode(
|
||||||
|
content,
|
||||||
|
BarcodeFormat.QR_CODE,
|
||||||
|
QR_CODE_SIZE,
|
||||||
|
QR_CODE_SIZE,
|
||||||
|
hints
|
||||||
|
);
|
||||||
|
|
||||||
|
// 转换为BufferedImage
|
||||||
|
BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix);
|
||||||
|
|
||||||
|
// 写入字节流并转换为Base64
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(image, IMAGE_FORMAT, bos);
|
||||||
|
|
||||||
|
// 返回带前缀的Base64字符串(前端可直接显示)
|
||||||
|
return "data:image/png;base64," + Base64Utils.encodeToString(bos.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/main/resources/application-dev.yml
Normal file
52
src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
server:
|
||||||
|
port: 8090
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: discipline-report
|
||||||
|
# 数据库
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://10.73.199.164:1444/finedb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: bigdata
|
||||||
|
password: bigdata@2020
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
# 邮箱配置
|
||||||
|
mail:
|
||||||
|
host: smtp.163.com
|
||||||
|
username: 18817503105@163.com
|
||||||
|
password: NScs2Ct8YTniyBNP
|
||||||
|
port: 465
|
||||||
|
protocol: smtps
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
smtp:
|
||||||
|
auth: true
|
||||||
|
starttls:
|
||||||
|
enable: true
|
||||||
|
required: true
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 50MB
|
||||||
|
max-request-size: 50MB
|
||||||
|
|
||||||
|
# Mybatis
|
||||||
|
mybatis:
|
||||||
|
mapper-locations: classpath:mapper/*.xml
|
||||||
|
type-aliases-package: com.discipline.report.entity
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
|
# 自定义配置【和CustomConfigProperties字段完全对齐】
|
||||||
|
custom:
|
||||||
|
h5-index-url: http://localhost
|
||||||
|
tip-content: 请如实填写举报信息,我们将严格保密
|
||||||
|
qr-code-fixed-url: http://10.15.33.107:3301/#/home
|
||||||
|
email-from: 18817503105@163.com
|
||||||
|
email-subject: 【系统提醒】新举报工单提交
|
||||||
|
email-content: <p>系统收到一条新举报,请及时处理!</p>
|
||||||
|
|
||||||
|
# 文件上传(Windows开发路径)
|
||||||
|
discipline:
|
||||||
|
file:
|
||||||
|
upload:
|
||||||
|
path: D:/home/upload/report
|
||||||
49
src/main/resources/application-prod.yml
Normal file
49
src/main/resources/application-prod.yml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
server:
|
||||||
|
port: 8090
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: discipline-report
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://10.73.199.144:3506/finedb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: bigdata
|
||||||
|
password: bigdata@2020
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
mail:
|
||||||
|
host: smtp.163.com
|
||||||
|
username: 18817503105@163.com
|
||||||
|
password: NScs2Ct8YTniyBNP
|
||||||
|
port: 465
|
||||||
|
protocol: smtps
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
smtp:
|
||||||
|
auth: true
|
||||||
|
starttls:
|
||||||
|
enable: true
|
||||||
|
required: true
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 50MB
|
||||||
|
max-request-size: 50MB
|
||||||
|
|
||||||
|
mybatis:
|
||||||
|
mapper-locations: classpath:mapper/*.xml
|
||||||
|
type-aliases-package: com.discipline.report.entity
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
|
# 自定义配置
|
||||||
|
custom:
|
||||||
|
h5-index-url: https://isee.ahtrq.com
|
||||||
|
tip-content: 请如实填写举报信息,我们将严格保密
|
||||||
|
qr-code-fixed-url: https://isee.ahtrq.com:9081/gmo
|
||||||
|
email-from: 18817503105@163.com
|
||||||
|
email-subject: 【系统提醒】新举报工单提交
|
||||||
|
email-content: <p>系统收到一条新举报,请及时处理!</p>
|
||||||
|
|
||||||
|
# Linux生产路径
|
||||||
|
discipline:
|
||||||
|
file:
|
||||||
|
upload:
|
||||||
|
path: /opt/ahtrq/discipline-report/upload/report
|
||||||
3
src/main/resources/application.yml
Normal file
3
src/main/resources/application.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: prod
|
||||||
32
src/main/resources/mapper/ReportEmailReceiverMapper.xml
Normal file
32
src/main/resources/mapper/ReportEmailReceiverMapper.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?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.discipline.report.mapper.ReportEmailReceiverMapper">
|
||||||
|
<resultMap id="BaseResultMap" type="com.discipline.report.entity.ReportEmailReceiver">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="receiver_name" property="receiverName"/>
|
||||||
|
<result column="receiver_email" property="receiverEmail"/>
|
||||||
|
<result column="receive_type" property="receiveType"/>
|
||||||
|
<result column="sort_num" property="sortNum"/>
|
||||||
|
<result column="status" property="status"/>
|
||||||
|
<result column="create_time" property="createTime"/>
|
||||||
|
<result column="update_time" property="updateTime"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="Base_Column_List">
|
||||||
|
id, receiver_name, receiver_email, receive_type, sort_num, status, create_time, update_time
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectByReceiveType" resultMap="BaseResultMap">
|
||||||
|
SELECT <include refid="Base_Column_List"/>
|
||||||
|
FROM report_email_receiver
|
||||||
|
WHERE status = 1 AND receive_type = #{receiveType}
|
||||||
|
ORDER BY sort_num ASC, id ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectAllEnabled" resultMap="BaseResultMap">
|
||||||
|
SELECT <include refid="Base_Column_List"/>
|
||||||
|
FROM report_email_receiver
|
||||||
|
WHERE status = 1
|
||||||
|
ORDER BY receive_type ASC, sort_num ASC, id ASC
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
19
src/main/resources/mapper/ReportFileMapper.xml
Normal file
19
src/main/resources/mapper/ReportFileMapper.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?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.discipline.report.mapper.ReportFileMapper">
|
||||||
|
<insert id="insertBatch">
|
||||||
|
INSERT INTO report_file (report_id, file_name, file_path, file_size, create_time)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="files" item="file" separator=",">
|
||||||
|
(#{file.reportId}, #{file.fileName}, #{file.filePath}, #{file.fileSize}, #{file.createTime})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<select id="selectByReportId" resultType="com.discipline.report.entity.ReportFile">
|
||||||
|
SELECT id, report_id, file_name, file_path, file_size, create_time
|
||||||
|
FROM report_file
|
||||||
|
WHERE report_id = #{reportId}
|
||||||
|
ORDER BY create_time DESC
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
14
src/main/resources/mapper/ReportRecordMapper.xml
Normal file
14
src/main/resources/mapper/ReportRecordMapper.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?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.discipline.report.mapper.ReportRecordMapper">
|
||||||
|
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO report_record (is_anonymous, reporter_name, report_content, contact_info, email_status, create_time)
|
||||||
|
VALUES (#{isAnonymous}, #{reporterName}, #{reportContent}, #{contactInfo}, #{emailStatus}, #{createTime})
|
||||||
|
</insert>
|
||||||
|
<update id="updateById">
|
||||||
|
UPDATE report_record
|
||||||
|
SET email_status = #{emailStatus}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
</mapper>
|
||||||
6
src/main/resources/static/index.html
Normal file
6
src/main/resources/static/index.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>hello word!!!</h1>
|
||||||
|
<p>this is a html page</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.discipline.report;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class DisciplineReportApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user