当前位置: 首页 > news >正文

企业级Java项目金融应用领域——银行系统(补充)

7. 报表与分析

财务报表

业务分析报表

监管报表

数据可视化

<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- Reporting --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><!-- Other --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-ui</artifactId><version>1.6.14</version></dependency>
</dependencies>
spring:datasource:url: jdbc:mysql://localhost:3306/bank_reporting_db?useSSL=false&serverTimezone=UTCusername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driverreporting:output-directory: ./reportsretention-days: 30server:port: 8080
@Entity
@Table(name = "report_definition")
@Data
public class ReportDefinition {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true)private String reportCode;@Column(nullable = false)private String reportName;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportType reportType;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportFrequency frequency;@Column(nullable = false, columnDefinition = "TEXT")private String sqlQuery;@Column(nullable = false)private String outputFields;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportFormat defaultFormat;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportStatus status = ReportStatus.ACTIVE;@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;
}
@Entity
@Table(name = "generated_report")
@Data
public class GeneratedReport {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false)private String reportCode;@Column(nullable = false)private String reportName;@Column(nullable = false)private LocalDate reportDate;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportFormat format;@Column(nullable = false)private String filePath;@Column(nullable = false)private Long fileSize;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportStatus status = ReportStatus.PENDING;@Columnprivate String generatedBy;@CreationTimestampprivate LocalDateTime createdAt;
}
@Entity
@Table(name = "report_schedule")
@Data
public class ReportSchedule {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false)private String reportCode;@Enumerated(EnumType.STRING)@Column(nullable = false)private ReportFrequency frequency;@Columnprivate LocalTime dailyTime;@Columnprivate Integer dayOfMonth;@Columnprivate Integer weekDay;@Enumerated(EnumType.STRING)@Column(nullable = false)private ScheduleStatus status = ScheduleStatus.ACTIVE;@Columnprivate String recipients;@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;
}
public enum ReportType {FINANCIAL,          // 财务报表BUSINESS,           // 业务报表REGULATORY,         // 监管报表OPERATIONAL,        // 运营报表ANALYTICAL          // 分析报表
}public enum ReportFrequency {DAILY,              // 每日WEEKLY,             // 每周MONTHLY,            // 每月QUARTERLY,          // 每季度YEARLY,             // 每年ON_DEMAND           // 按需
}public enum ReportFormat {PDF,                // PDF格式EXCEL,              // Excel格式CSV,                // CSV格式HTML,               // HTML格式JSON                // JSON格式
}public enum ReportStatus {ACTIVE,             // 活跃INACTIVE,           // 不活跃PENDING,            // 待处理GENERATING,         // 生成中COMPLETED,          // 已完成FAILED              // 失败
}public enum ScheduleStatus {ACTIVE,             // 活跃INACTIVE,           // 不活跃PAUSED              // 暂停
}
@Data
public class ReportDefinitionDTO {private Long id;private String reportCode;private String reportName;private ReportType reportType;private ReportFrequency frequency;private String sqlQuery;private String outputFields;private ReportFormat defaultFormat;private ReportStatus status;private LocalDateTime createdAt;private LocalDateTime updatedAt;
}
@Data
public class ReportRequestDTO {@NotNullprivate String reportCode;private LocalDate reportDate;@NotNullprivate ReportFormat format;private String parameters; // JSON格式的参数
}
@Data
public class ReportRequestDTO {@NotNullprivate String reportCode;private LocalDate reportDate;@NotNullprivate ReportFormat format;private String parameters; // JSON格式的参数
}
@Data
public class ReportScheduleDTO {private Long id;private String reportCode;private ReportFrequency frequency;private LocalTime dailyTime;private Integer dayOfMonth;private Integer weekDay;private ScheduleStatus status;private String recipients;private LocalDateTime createdAt;private LocalDateTime updatedAt;
}
@Data
public class ReportDataDTO {private String reportCode;private String reportName;private List<String> headers;private List<Map<String, Object>> data;private long totalRecords;
}
@Data
public class DashboardDataDTO {private BigDecimal totalDeposits;private BigDecimal totalLoans;private BigDecimal totalAssets;private BigDecimal totalLiabilities;private BigDecimal netIncome;private Map<LocalDate, BigDecimal> dailyTransactions;private Map<String, Long> customerDistribution;private List<Map<String, Object>> recentTransactions;private List<Map<String, Object>> loanPortfolio;
}

Repository层

public interface ReportDefinitionRepository extends JpaRepository<ReportDefinition, Long> {Optional<ReportDefinition> findByReportCode(String reportCode);List<ReportDefinition> findByReportType(ReportType reportType);List<ReportDefinition> findByStatus(ReportStatus status);
}
public interface GeneratedReportRepository extends JpaRepository<GeneratedReport, Long> {List<GeneratedReport> findByReportCodeAndReportDateBetween(String reportCode, LocalDate startDate, LocalDate endDate);List<GeneratedReport> findByReportCodeAndFormatAndStatus(String reportCode, ReportFormat format, ReportStatus status);Optional<GeneratedReport> findByReportCodeAndReportDateAndFormat(String reportCode, LocalDate reportDate, ReportFormat format);
}
public interface ReportScheduleRepository extends JpaRepository<ReportSchedule, Long> {List<ReportSchedule> findByReportCode(String reportCode);List<ReportSchedule> findByFrequencyAndStatus(ReportFrequency frequency, ScheduleStatus status);
}

Service层

@Service
@RequiredArgsConstructor
@Slf4j
public class ReportGenerationService {private final ReportDefinitionRepository definitionRepository;private final GeneratedReportRepository generatedReportRepository;private final ReportQueryExecutor queryExecutor;private final ReportFileGenerator fileGenerator;@Transactionalpublic GeneratedReportDTO generateReport(ReportRequestDTO request) {LocalDate reportDate = request.getReportDate() != null ? request.getReportDate() : LocalDate.now();// 检查是否已存在相同报告Optional<GeneratedReport> existingReport = generatedReportRepository.findByReportCodeAndReportDateAndFormat(request.getReportCode(), reportDate, request.getFormat());if (existingReport.isPresent()) {return convertToDTO(existingReport.get());}// 获取报告定义ReportDefinition definition = definitionRepository.findByReportCode(request.getReportCode()).orElseThrow(() -> new AccountException(ErrorCode.REPORT_DEFINITION_NOT_FOUND));// 创建报告记录GeneratedReport report = new GeneratedReport();report.setReportCode(definition.getReportCode());report.setReportName(definition.getReportName());report.setReportDate(reportDate);report.setFormat(request.getFormat());report.setStatus(ReportStatus.PENDING);GeneratedReport saved = generatedReportRepository.save(report);log.info("Report generation requested: {}", saved.getId());// 异步生成报告generateReportAsync(saved.getId(), definition, request.getParameters());return convertToDTO(saved);}@Asyncpublic void generateReportAsync(Long reportId, ReportDefinition definition, String parameters) {try {// 更新状态为生成中GeneratedReport report = generatedReportRepository.findById(reportId).orElseThrow(() -> new AccountException(ErrorCode.REPORT_NOT_FOUND));report.setStatus(ReportStatus.GENERATING);generatedReportRepository.save(report);// 执行SQL查询获取数据Map<String, Object> params = parseParameters(parameters);ReportDataDTO reportData = queryExecutor.executeQuery(definition.getSqlQuery(), params);// 生成报告文件String filePath = fileGenerator.generateReportFile(reportData, definition.getOutputFields(), report.getFormat(),definition.getReportCode(),report.getReportDate());// 更新报告记录report.setFilePath(filePath);report.setFileSize(getFileSize(filePath));report.setStatus(ReportStatus.COMPLETED);generatedReportRepository.save(report);log.info("Report generated successfully: {}", reportId);} catch (Exception e) {log.error("Failed to generate report: " + reportId, e);generatedReportRepository.findById(reportId).ifPresent(r -> {r.setStatus(ReportStatus.FAILED);generatedReportRepository.save(r);});}}@Transactional(readOnly = true)public ReportDataDTO getReportData(String reportCode, String parameters) {ReportDefinition definition = definitionRepository.findByReportCode(reportCode).orElseThrow(() -> new AccountException(ErrorCode.REPORT_DEFINITION_NOT_FOUND));Map<String, Object> params = parseParameters(parameters);return queryExecutor.executeQuery(definition.getSqlQuery(), params);}private Map<String, Object> parseParameters(String parameters) {// 实现JSON参数解析// 实际项目中可以使用Jackson或Gsonreturn Map.of(); // 简化处理}private long getFileSize(String filePath) {// 实现获取文件大小逻辑return 0; // 简化处理}private GeneratedReportDTO convertToDTO(GeneratedReport report) {GeneratedReportDTO dto = new GeneratedReportDTO();dto.setId(report.getId());dto.setReportCode(report.getReportCode());dto.setReportName(report.getReportName());dto.setReportDate(report.getReportDate());dto.setFormat(report.getFormat());dto.setFilePath(report.getFilePath());dto.setFileSize(report.getFileSize());dto.setStatus(report.getStatus());dto.setGeneratedBy(report.getGeneratedBy());dto.setCreatedAt(report.getCreatedAt());return dto;}
}
/**报表查询执行器
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class ReportQueryExecutor {private final JdbcTemplate jdbcTemplate;public ReportDataDTO executeQuery(String sqlQuery, Map<String, Object> parameters) {try {ReportDataDTO result = new ReportDataDTO();List<Map<String, Object>> data = new ArrayList<>();// 执行查询SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sqlQuery, parameters);// 获取列信息List<String> headers = new ArrayList<>();if (rowSet.next()) {String[] columnNames = rowSet.getMetaData().getColumnNames();for (String columnName : columnNames) {headers.add(columnName);}// 处理第一行数据data.add(extractRowData(rowSet, headers));// 处理剩余数据while (rowSet.next()) {data.add(extractRowData(rowSet, headers));}}result.setHeaders(headers);result.setData(data);result.setTotalRecords(data.size());return result;} catch (Exception e) {log.error("Error executing report query: " + e.getMessage(), e);throw new AccountException(ErrorCode.REPORT_QUERY_EXECUTION_FAILED);}}private Map<String, Object> extractRowData(SqlRowSet rowSet, List<String> headers) {Map<String, Object> rowData = new HashMap<>();for (String column : headers) {rowData.put(column, rowSet.getObject(column));}return rowData;}
}
/**报表文件生成器
*/
@Component
@Slf4j
public class ReportFileGenerator {@Value("${reporting.output-directory}")private String outputDirectory;public String generateReportFile(ReportDataDTO reportData,String outputFields,ReportFormat format,String reportCode,LocalDate reportDate) throws IOException {String fileName = String.format("%s_%s.%s", reportCode, reportDate.format(DateTimeFormatter.BASIC_ISO_DATE), format.name().toLowerCase());Path outputPath = Paths.get(outputDirectory, fileName);switch (format) {case EXCEL:generateExcelFile(reportData, outputPath);break;case PDF:// 实现PDF生成逻辑break;case CSV:// 实现CSV生成逻辑break;case HTML:// 实现HTML生成逻辑break;case JSON:// 实现JSON生成逻辑break;default:throw new IllegalArgumentException("Unsupported report format: " + format);}return outputPath.toString();}private void generateExcelFile(ReportDataDTO reportData, Path outputPath) throws IOException {try (Workbook workbook = new XSSFWorkbook()) {Sheet sheet = workbook.createSheet("Report");// 创建标题行Row headerRow = sheet.createRow(0);List<String> headers = reportData.getHeaders();for (int i = 0; i < headers.size(); i++) {Cell cell = headerRow.createCell(i);cell.setCellValue(headers.get(i));}// 填充数据List<Map<String, Object>> data = reportData.getData();for (int i = 0; i < data.size(); i++) {Row row = sheet.createRow(i + 1);Map<String, Object> rowData = data.get(i);for (int j = 0; j < headers.size(); j++) {Cell cell = row.createCell(j);Object value = rowData.get(headers.get(j));if (value instanceof Number) {cell.setCellValue(((Number) value).doubleValue());} else {cell.setCellValue(value != null ? value.toString() : "");}}}// 自动调整列宽for (int i = 0; i < headers.size(); i++) {sheet.autoSizeColumn(i);}// 写入文件try (FileOutputStream outputStream = new FileOutputStream(outputPath.toFile())) {workbook.write(outputStream);}}}
}
/**定时任务服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ReportSchedulingService {private final ReportScheduleRepository scheduleRepository;private final ReportGenerationService generationService;@Scheduled(cron = "0 0 6 * * ?") // 每天6点执行public void generateDailyReports() {log.info("Starting daily report generation");List<ReportSchedule> schedules = scheduleRepository.findByFrequencyAndStatus(ReportFrequency.DAILY, ScheduleStatus.ACTIVE);schedules.forEach(schedule -> {try {generateScheduledReport(schedule);} catch (Exception e) {log.error("Failed to generate daily report: " + schedule.getReportCode(), e);}});}@Scheduled(cron = "0 0 6 * * MON") // 每周一6点执行public void generateWeeklyReports() {log.info("Starting weekly report generation");List<ReportSchedule> schedules = scheduleRepository.findByFrequencyAndStatus(ReportFrequency.WEEKLY, ScheduleStatus.ACTIVE);schedules.forEach(schedule -> {try {generateScheduledReport(schedule);} catch (Exception e) {log.error("Failed to generate weekly report: " + schedule.getReportCode(), e);}});}@Scheduled(cron = "0 0 6 1 * ?") // 每月1号6点执行public void generateMonthlyReports() {log.info("Starting monthly report generation");List<ReportSchedule> schedules = scheduleRepository.findByFrequencyAndStatus(ReportFrequency.MONTHLY, ScheduleStatus.ACTIVE);schedules.forEach(schedule -> {try {generateScheduledReport(schedule);} catch (Exception e) {log.error("Failed to generate monthly report: " + schedule.getReportCode(), e);}});}private void generateScheduledReport(ReportSchedule schedule) {ReportRequestDTO request = new ReportRequestDTO();request.setReportCode(schedule.getReportCode());request.setFormat(ReportFormat.EXCEL); // 默认生成Excel格式generationService.generateReport(request);log.info("Scheduled report generated: {}", schedule.getReportCode());}
}
/**数据可视化服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class DashboardService {private final JdbcTemplate jdbcTemplate;public DashboardDataDTO getDashboardData() {DashboardDataDTO dashboard = new DashboardDataDTO();// 获取总存款dashboard.setTotalDeposits(getTotalDeposits());// 获取总贷款dashboard.setTotalLoans(getTotalLoans());// 获取总资产和总负债Map<String, BigDecimal> assetsAndLiabilities = getAssetsAndLiabilities();dashboard.setTotalAssets(assetsAndLiabilities.get("assets"));dashboard.setTotalLiabilities(assetsAndLiabilities.get("liabilities"));// 获取净收入dashboard.setNetIncome(getNetIncome());// 获取每日交易量dashboard.setDailyTransactions(getDailyTransactions());// 获取客户分布dashboard.setCustomerDistribution(getCustomerDistribution());// 获取最近交易dashboard.setRecentTransactions(getRecentTransactions());// 获取贷款组合dashboard.setLoanPortfolio(getLoanPortfolio());return dashboard;}private BigDecimal getTotalDeposits() {String sql = "SELECT SUM(balance) FROM account WHERE account_type IN ('PERSONAL_SAVINGS', 'PERSONAL_CURRENT', 'CORPORATE_CURRENT')";return jdbcTemplate.queryForObject(sql, BigDecimal.class);}private BigDecimal getTotalLoans() {String sql = "SELECT SUM(outstanding_amount) FROM loan_account WHERE status = 'ACTIVE'";return jdbcTemplate.queryForObject(sql, BigDecimal.class);}private Map<String, BigDecimal> getAssetsAndLiabilities() {String sql = "SELECT 'assets' AS type, SUM(balance) AS amount FROM account " +"UNION ALL " +"SELECT 'liabilities' AS type, SUM(outstanding_amount) AS amount FROM loan_account";return jdbcTemplate.query(sql, rs -> {Map<String, BigDecimal> result = new HashMap<>();while (rs.next()) {result.put(rs.getString("type"), rs.getBigDecimal("amount"));}return result;});}private BigDecimal getNetIncome() {String sql = "SELECT (SELECT SUM(amount) FROM transaction WHERE amount > 0 AND created_at >= DATE_TRUNC('month', CURRENT_DATE)) - " +"(SELECT SUM(amount) FROM transaction WHERE amount < 0 AND created_at >= DATE_TRUNC('month', CURRENT_DATE))";return jdbcTemplate.queryForObject(sql, BigDecimal.class);}private Map<LocalDate, BigDecimal> getDailyTransactions() {String sql = "SELECT DATE(created_at) AS day, SUM(ABS(amount)) AS volume " +"FROM transaction " +"WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' " +"GROUP BY day " +"ORDER BY day";return jdbcTemplate.query(sql, rs -> {Map<LocalDate, BigDecimal> result = new HashMap<>();while (rs.next()) {result.put(rs.getDate("day").toLocalDate(), rs.getBigDecimal("volume"));}return result;});}private Map<String, Long> getCustomerDistribution() {String sql = "SELECT tier, COUNT(*) AS count FROM customer GROUP BY tier";return jdbcTemplate.query(sql, rs -> {Map<String, Long> result = new HashMap<>();while (rs.next()) {result.put(rs.getString("tier"), rs.getLong("count"));}return result;});}private List<Map<String, Object>> getRecentTransactions() {String sql = "SELECT t.id, t.amount, t.description, a.account_number, c.first_name, c.last_name " +"FROM transaction t " +"JOIN account a ON t.account_number = a.account_number " +"JOIN customer c ON a.customer_id = c.id " +"ORDER BY t.created_at DESC LIMIT 10";return jdbcTemplate.queryForList(sql);}private List<Map<String, Object>> getLoanPortfolio() {String sql = "SELECT loan_type, SUM(outstanding_amount) AS amount, COUNT(*) AS count " +"FROM loan_account " +"WHERE status = 'ACTIVE' " +"GROUP BY loan_type";return jdbcTemplate.queryForList(sql);}
}

Controller层

@RestController
@RequestMapping("/api/reports")
@RequiredArgsConstructor
public class ReportController {private final ReportGenerationService generationService;private final ReportSchedulingService schedulingService;@PostMapping("/generate")@PreAuthorize("hasAnyRole('REPORT_USER', 'ADMIN')")public GeneratedReportDTO generateReport(@Valid @RequestBody ReportRequestDTO request) {return generationService.generateReport(request);}@GetMapping("/data/{reportCode}")@PreAuthorize("hasAnyRole('REPORT_USER', 'ADMIN')")public ReportDataDTO getReportData(@PathVariable String reportCode,@RequestParam(required = false) String parameters) {return generationService.getReportData(reportCode, parameters);}
}// DashboardController.java
package com.bank.reporting.controller;import com.bank.reporting.dto.DashboardDataDTO;
import com.bank.reporting.service.DashboardService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/dashboard")
@RequiredArgsConstructor
public class DashboardController {private final DashboardService dashboardService;@GetMappingpublic DashboardDataDTO getDashboardData() {return dashboardService.getDashboardData();}
}

8. 系统管理

用户权限管理

角色管理

系统参数配置

操作日志审计

@Entity
@Table(name = "system_user")
@Data
public class SystemUser {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true)private String username;@Column(nullable = false)private String password;@Column(nullable = false)private String fullName;@Column(nullable = false, unique = true)private String email;@Columnprivate String phone;@Column(nullable = false)private String department;@Enumerated(EnumType.STRING)@Column(nullable = false)private UserStatus status = UserStatus.ACTIVE;@ManyToMany(fetch = FetchType.EAGER)@JoinTable(name = "user_role",joinColumns = @JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))private Set<SystemRole> roles = new HashSet<>();@Columnprivate LocalDateTime lastLoginTime;@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;@Versionprivate Long version;
}
@Entity
@Table(name = "system_role")
@Data
public class SystemRole {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true)private String roleCode;@Column(nullable = false)private String roleName;@Columnprivate String description;@ManyToMany(fetch = FetchType.EAGER)@JoinTable(name = "role_permission",joinColumns = @JoinColumn(name = "role_id"),inverseJoinColumns = @JoinColumn(name = "permission_id"))private Set<SystemPermission> permissions = new HashSet<>();@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;
}
@Entity
@Table(name = "system_permission")
@Data
public class SystemPermission {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true)private String permissionCode;@Column(nullable = false)private String permissionName;@Columnprivate String description;@Column(nullable = false)private String resource;@Column(nullable = false)private String action;@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;
}
@Entity
@Table(name = "system_parameter")
@Data
public class SystemParameter {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false, unique = true)private String paramKey;@Column(nullable = false)private String paramName;@Column(nullable = false)private String paramValue;@Columnprivate String description;@Column(nullable = false)private Boolean isEditable = true;@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;
}
@Entity
@Table(name = "audit_log")
@Data
public class AuditLog {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false)private Long userId;@Column(nullable = false)private String username;@Enumerated(EnumType.STRING)@Column(nullable = false)private AuditAction action;@Column(nullable = false)private String entityType;@Columnprivate Long entityId;@Column(columnDefinition = "TEXT")private String oldValue;@Column(columnDefinition = "TEXT")private String newValue;@Columnprivate String ipAddress;@Columnprivate String userAgent;@CreationTimestampprivate LocalDateTime createdAt;
}
public enum UserStatus {ACTIVE,         // 活跃INACTIVE,       // 不活跃LOCKED,         // 锁定PASSWORD_EXPIRED // 密码过期
}public enum AuditAction {CREATE,         // 创建UPDATE,         // 更新DELETE,         // 删除LOGIN,          // 登录LOGOUT,         // 登出ACCESS          // 访问
}
public enum AuditAction {CREATE,         // 创建UPDATE,         // 更新DELETE,         // 删除LOGIN,          // 登录LOGOUT,         // 登出ACCESS          // 访问
}
@Data
public class UserCreateRequest {@NotBlankprivate String username;@NotBlank@Size(min = 8)private String password;@NotBlankprivate String fullName;@Email@NotBlankprivate String email;private String phone;@NotBlankprivate String department;
}
@Data
public class UserUpdateRequest {@NotBlankprivate String fullName;@Email@NotBlankprivate String email;private String phone;@NotBlankprivate String department;
}
@Data
public class RoleDTO {private Long id;private String roleCode;private String roleName;private String description;private Set<String> permissions;private LocalDateTime createdAt;
}
@Data
public class RoleDTO {private Long id;private String roleCode;private String roleName;private String description;private Set<String> permissions;private LocalDateTime createdAt;
}
@Data
public class ParameterDTO {private Long id;private String paramKey;private String paramName;private String paramValue;private String description;private Boolean isEditable;private LocalDateTime createdAt;
}
@Data
public class AuditLogDTO {private Long id;private Long userId;private String username;private AuditAction action;private String entityType;private Long entityId;private String oldValue;private String newValue;private String ipAddress;private String userAgent;private LocalDateTime createdAt;
}

Repository层

public interface UserRepository extends JpaRepository<SystemUser, Long> {Optional<SystemUser> findByUsername(String username);Optional<SystemUser> findByEmail(String email);boolean existsByUsername(String username);boolean existsByEmail(String email);@Query("SELECT u FROM SystemUser u JOIN u.roles r WHERE r.roleCode = :roleCode")List<SystemUser> findByRoleCode(@Param("roleCode") String roleCode);
}
public interface RoleRepository extends JpaRepository<SystemRole, Long> {Optional<SystemRole> findByRoleCode(String roleCode);boolean existsByRoleCode(String roleCode);
}
public interface RoleRepository extends JpaRepository<SystemRole, Long> {Optional<SystemRole> findByRoleCode(String roleCode);boolean existsByRoleCode(String roleCode);
}
public interface ParameterRepository extends JpaRepository<SystemParameter, Long> {Optional<SystemParameter> findByParamKey(String paramKey);boolean existsByParamKey(String paramKey);
}
public interface AuditLogRepository extends JpaRepository<AuditLog, Long> {List<AuditLog> findByUserId(Long userId);List<AuditLog> findByAction(AuditAction action);List<AuditLog> findByEntityTypeAndEntityId(String entityType, Long entityId);List<AuditLog> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
}

Service层

@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {private final UserRepository userRepository;private final RoleRepository roleRepository;private final PasswordEncoder passwordEncoder;private final AuditLogService auditLogService;@Transactional@PreAuthorize("hasRole('ADMIN')")public UserDTO createUser(UserCreateRequest request) {if (userRepository.existsByUsername(request.getUsername())) {throw new AccountException(ErrorCode.USERNAME_ALREADY_EXISTS);}if (userRepository.existsByEmail(request.getEmail())) {throw new AccountException(ErrorCode.EMAIL_ALREADY_EXISTS);}SystemUser user = new SystemUser();user.setUsername(request.getUsername());user.setPassword(passwordEncoder.encode(request.getPassword()));user.setFullName(request.getFullName());user.setEmail(request.getEmail());user.setPhone(request.getPhone());user.setDepartment(request.getDepartment());user.setStatus(UserStatus.ACTIVE);SystemUser saved = userRepository.save(user);log.info("User created: {}", saved.getUsername());auditLogService.logUserAction(saved.getId(), "CREATE", "User created");return convertToDTO(saved);}@Transactional@PreAuthorize("hasRole('ADMIN') or #username == authentication.principal.username")public UserDTO updateUser(String username, UserUpdateRequest request) {SystemUser user = userRepository.findByUsername(username).orElseThrow(() -> new AccountException(ErrorCode.USER_NOT_FOUND));user.setFullName(request.getFullName());user.setEmail(request.getEmail());user.setPhone(request.getPhone());user.setDepartment(request.getDepartment());SystemUser saved = userRepository.save(user);log.info("User updated: {}", username);auditLogService.logUserAction(saved.getId(), "UPDATE", "User updated");return convertToDTO(saved);}@Transactional@PreAuthorize("hasRole('ADMIN')")public UserDTO updateUserStatus(String username, UserStatus status) {SystemUser user = userRepository.findByUsername(username).orElseThrow(() -> new AccountException(ErrorCode.USER_NOT_FOUND));user.setStatus(status);SystemUser saved = userRepository.save(user);log.info("User status updated to {}: {}", status, username);auditLogService.logUserAction(saved.getId(), "UPDATE", "Status updated to " + status);return convertToDTO(saved);}@Transactional@PreAuthorize("hasRole('ADMIN') or #username == authentication.principal.username")public void changePassword(String username, String newPassword) {SystemUser user = userRepository.findByUsername(username).orElseThrow(() -> new AccountException(ErrorCode.USER_NOT_FOUND));user.setPassword(passwordEncoder.encode(newPassword));userRepository.save(user);log.info("Password changed for user: {}", username);auditLogService.logUserAction(user.getId(), "UPDATE", "Password changed");}@Transactional@PreAuthorize("hasRole('ADMIN')")public UserDTO assignRoles(String username, Set<String> roleCodes) {SystemUser user = userRepository.findByUsername(username).orElseThrow(() -> new AccountException(ErrorCode.USER_NOT_FOUND));Set<SystemRole> roles = roleCodes.stream().map(roleCode -> roleRepository.findByRoleCode(roleCode).orElseThrow(() -> new AccountException(ErrorCode.ROLE_NOT_FOUND))).collect(Collectors.toSet());user.setRoles(roles);SystemUser saved = userRepository.save(user);log.info("Roles assigned to user {}: {}", username, roleCodes);auditLogService.logUserAction(saved.getId(), "UPDATE", "Roles assigned: " + roleCodes);return convertToDTO(saved);}@Transactional(readOnly = true)@PreAuthorize("hasRole('ADMIN') or #username == authentication.principal.username")public UserDTO getUser(String username) {SystemUser user = userRepository.findByUsername(username).orElseThrow(() -> new AccountException(ErrorCode.USER_NOT_FOUND));return convertToDTO(user);}@Transactional(readOnly = true)@PreAuthorize("hasRole('ADMIN')")public List<UserDTO> getAllUsers() {return userRepository.findAll().stream().map(this::convertToDTO).collect(Collectors.toList());}@Transactional(readOnly = true)@PreAuthorize("hasRole('ADMIN')")public List<UserDTO> getUsersByRole(String roleCode) {return userRepository.findByRoleCode(roleCode).stream().map(this::convertToDTO).collect(Collectors.toList());}private UserDTO convertToDTO(SystemUser user) {UserDTO dto = new UserDTO();dto.setId(user.getId());dto.setUsername(user.getUsername());dto.setFullName(user.getFullName());dto.setEmail(user.getEmail());dto.setPhone(user.getPhone());dto.setDepartment(user.getDepartment());dto.setStatus(user.getStatus());dto.setRoles(user.getRoles().stream().map(SystemRole::getRoleCode).collect(Collectors.toSet()));dto.setLastLoginTime(user.getLastLoginTime());dto.setCreatedAt(user.getCreatedAt());return dto;}
}
/**角色与权限服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class RoleService {private final RoleRepository roleRepository;private final PermissionRepository permissionRepository;private final AuditLogService auditLogService;@Transactional@PreAuthorize("hasRole('ADMIN')")public RoleDTO createRole(String roleCode, String roleName, String description) {if (roleRepository.existsByRoleCode(roleCode)) {throw new AccountException(ErrorCode.ROLE_ALREADY_EXISTS);}SystemRole role = new SystemRole();role.setRoleCode(roleCode);role.setRoleName(roleName);role.setDescription(description);SystemRole saved = roleRepository.save(role);log.info("Role created: {}", roleCode);auditLogService.logRoleAction(saved.getId(), "CREATE", "Role created");return convertToDTO(saved);}@Transactional@PreAuthorize("hasRole('ADMIN')")public RoleDTO updateRole(String roleCode, String roleName, String description) {SystemRole role = roleRepository.findByRoleCode(roleCode).orElseThrow(() -> new AccountException(ErrorCode.ROLE_NOT_FOUND));role.setRoleName(roleName);role.setDescription(description);SystemRole saved = roleRepository.save(role);log.info("Role updated: {}", roleCode);auditLogService.logRoleAction(saved.getId(), "UPDATE", "Role updated");return convertToDTO(saved);}@Transactional@PreAuthorize("hasRole('ADMIN')")public RoleDTO assignPermissions(String roleCode, Set<String> permissionCodes) {SystemRole role = roleRepository.findByRoleCode(roleCode).orElseThrow(() -> new AccountException(ErrorCode.ROLE_NOT_FOUND));Set<SystemPermission> permissions = permissionCodes.stream().map(code -> permissionRepository.findByPermissionCode(code).orElseThrow(() -> new AccountException(ErrorCode.PERMISSION_NOT_FOUND))).collect(Collectors.toSet());role.setPermissions(permissions);SystemRole saved = roleRepository.save(role);log.info("Permissions assigned to role {}: {}", roleCode, permissionCodes);auditLogService.logRoleAction(saved.getId(), "UPDATE", "Permissions assigned: " + permissionCodes);return convertToDTO(saved);}@Transactional(readOnly = true)public List<RoleDTO> getAllRoles() {return roleRepository.findAll().stream().map(this::convertToDTO).collect(Collectors.toList());}@Transactional(readOnly = true)public RoleDTO getRole(String roleCode) {SystemRole role = roleRepository.findByRoleCode(roleCode).orElseThrow(() -> new AccountException(ErrorCode.ROLE_NOT_FOUND));return convertToDTO(role);}private RoleDTO convertToDTO(SystemRole role) {RoleDTO dto = new RoleDTO();dto.setId(role.getId());dto.setRoleCode(role.getRoleCode());dto.setRoleName(role.getRoleName());dto.setDescription(role.getDescription());dto.setPermissions(role.getPermissions().stream().map(SystemPermission::getPermissionCode).collect(Collectors.toSet()));dto.setCreatedAt(role.getCreatedAt());return dto;}
}
/**系统参数服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ParameterService {private final ParameterRepository parameterRepository;private final AuditLogService auditLogService;@Transactional@PreAuthorize("hasRole('ADMIN')")public ParameterDTO createParameter(String key, String name, String value, String description, Boolean isEditable) {if (parameterRepository.existsByParamKey(key)) {throw new AccountException(ErrorCode.PARAMETER_ALREADY_EXISTS);}SystemParameter parameter = new SystemParameter();parameter.setParamKey(key);parameter.setParamName(name);parameter.setParamValue(value);parameter.setDescription(description);parameter.setIsEditable(isEditable);SystemParameter saved = parameterRepository.save(parameter);log.info("System parameter created: {}", key);auditLogService.logParameterAction(saved.getId(), "CREATE", "Parameter created");return convertToDTO(saved);}@Transactional@PreAuthorize("hasRole('ADMIN')")public ParameterDTO updateParameter(String key, String value, String description) {SystemParameter parameter = parameterRepository.findByParamKey(key).orElseThrow(() -> new AccountException(ErrorCode.PARAMETER_NOT_FOUND));if (!parameter.getIsEditable()) {throw new AccountException(ErrorCode.PARAMETER_NOT_EDITABLE);}parameter.setParamValue(value);parameter.setDescription(description);SystemParameter saved = parameterRepository.save(parameter);log.info("System parameter updated: {}", key);auditLogService.logParameterAction(saved.getId(), "UPDATE", "Parameter updated");return convertToDTO(saved);}@Transactional(readOnly = true)public ParameterDTO getParameter(String key) {SystemParameter parameter = parameterRepository.findByParamKey(key).orElseThrow(() -> new AccountException(ErrorCode.PARAMETER_NOT_FOUND));return convertToDTO(parameter);}@Transactional(readOnly = true)public List<ParameterDTO> getAllParameters() {return parameterRepository.findAll().stream().map(this::convertToDTO).collect(Collectors.toList());}private ParameterDTO convertToDTO(SystemParameter parameter) {ParameterDTO dto = new ParameterDTO();dto.setId(parameter.getId());dto.setParamKey(parameter.getParamKey());dto.setParamName(parameter.getParamName());dto.setParamValue(parameter.getParamValue());dto.setDescription(parameter.getDescription());dto.setIsEditable(parameter.getIsEditable());dto.setCreatedAt(parameter.getCreatedAt());return dto;}
}
/**审计日志服务
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class AuditLogService {private final AuditLogRepository auditLogRepository;@Transactionalpublic void logUserAction(Long userId, String action, String description) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String username = authentication.getName();HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();AuditLog logEntry = new AuditLog();logEntry.setUserId(userId);logEntry.setUsername(username);logEntry.setAction(AuditAction.valueOf(action));logEntry.setEntityType("USER");logEntry.setEntityId(userId);logEntry.setIpAddress(request.getRemoteAddr());logEntry.setUserAgent(request.getHeader("User-Agent"));auditLogRepository.save(logEntry);}@Transactionalpublic void logRoleAction(Long roleId, String action, String description) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String username = authentication.getName();HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();AuditLog logEntry = new AuditLog();logEntry.setUserId(getCurrentUserId());logEntry.setUsername(username);logEntry.setAction(AuditAction.valueOf(action));logEntry.setEntityType("ROLE");logEntry.setEntityId(roleId);logEntry.setIpAddress(request.getRemoteAddr());logEntry.setUserAgent(request.getHeader("User-Agent"));auditLogRepository.save(logEntry);}@Transactionalpublic void logParameterAction(Long parameterId, String action, String description) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String username = authentication.getName();HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();AuditLog logEntry = new AuditLog();logEntry.setUserId(getCurrentUserId());logEntry.setUsername(username);logEntry.setAction(AuditAction.valueOf(action));logEntry.setEntityType("PARAMETER");logEntry.setEntityId(parameterId);logEntry.setIpAddress(request.getRemoteAddr());logEntry.setUserAgent(request.getHeader("User-Agent"));auditLogRepository.save(logEntry);}@Transactional(readOnly = true)public List<AuditLogDTO> getLogsByUser(Long userId) {return auditLogRepository.findByUserId(userId).stream().map(this::convertToDTO).collect(Collectors.toList());}@Transactional(readOnly = true)public List<AuditLogDTO> getLogsByEntity(String entityType, Long entityId) {return auditLogRepository.findByEntityTypeAndEntityId(entityType, entityId).stream().map(this::convertToDTO).collect(Collectors.toList());}@Transactional(readOnly = true)public List<AuditLogDTO> getLogsBetweenDates(LocalDateTime start, LocalDateTime end) {return auditLogRepository.findByCreatedAtBetween(start, end).stream().map(this::convertToDTO).collect(Collectors.toList());}private Long getCurrentUserId() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();// 实际项目中需要从认证信息中获取用户IDreturn 1L; // 简化处理}private AuditLogDTO convertToDTO(AuditLog log) {AuditLogDTO dto = new AuditLogDTO();dto.setId(log.getId());dto.setUserId(log.getUserId());dto.setUsername(log.getUsername());dto.setAction(log.getAction());dto.setEntityType(log.getEntityType());dto.setEntityId(log.getEntityId());dto.setOldValue(log.getOldValue());dto.setNewValue(log.getNewValue());dto.setIpAddress(log.getIpAddress());dto.setUserAgent(log.getUserAgent());dto.setCreatedAt(log.getCreatedAt());return dto;}
}

Controller层

@RestController
@RequestMapping("/api/admin/users")
@RequiredArgsConstructor
public class UserController {private final UserService userService;@PostMapping@PreAuthorize("hasRole('ADMIN')")public UserDTO createUser(@Valid @RequestBody UserCreateRequest request) {return userService.createUser(request);}@PutMapping("/{username}")public UserDTO updateUser(@PathVariable String username,@Valid @RequestBody UserUpdateRequest request) {return userService.updateUser(username, request);}@PutMapping("/{username}/status/{status}")@PreAuthorize("hasRole('ADMIN')")public UserDTO updateUserStatus(@PathVariable String username,@PathVariable String status) {return userService.updateUserStatus(username, UserStatus.valueOf(status));}@PutMapping("/{username}/password")public void changePassword(@PathVariable String username,@RequestParam String newPassword) {userService.changePassword(username, newPassword);}@PutMapping("/{username}/roles")@PreAuthorize("hasRole('ADMIN')")public UserDTO assignRoles(@PathVariable String username,@RequestBody Set<String> roleCodes) {return userService.assignRoles(username, roleCodes);}@GetMapping("/{username}")public UserDTO getUser(@PathVariable String username) {return userService.getUser(username);}@GetMapping@PreAuthorize("hasRole('ADMIN')")public List<UserDTO> getAllUsers() {return userService.getAllUsers();}@GetMapping("/by-role/{roleCode}")@PreAuthorize("hasRole('ADMIN')")public List<UserDTO> getUsersByRole(@PathVariable String roleCode) {return userService.getUsersByRole(roleCode);}
}
@RestController
@RequestMapping("/api/admin/roles")
@RequiredArgsConstructor
public class RoleController {private final RoleService roleService;@PostMapping@PreAuthorize("hasRole('ADMIN')")public RoleDTO createRole(@RequestParam String roleCode,@RequestParam String roleName,@RequestParam(required = false) String description) {return roleService.createRole(roleCode, roleName, description);}@PutMapping("/{roleCode}")@PreAuthorize("hasRole('ADMIN')")public RoleDTO updateRole(@PathVariable String roleCode,@RequestParam String roleName,@RequestParam(required = false) String description) {return roleService.updateRole(roleCode, roleName, description);}@PutMapping("/{roleCode}/permissions")@PreAuthorize("hasRole('ADMIN')")public RoleDTO assignPermissions(@PathVariable String roleCode,@RequestBody Set<String> permissionCodes) {return roleService.assignPermissions(roleCode, permissionCodes);}@GetMapping("/{roleCode}")public RoleDTO getRole(@PathVariable String roleCode) {return roleService.getRole(roleCode);}@GetMappingpublic List<RoleDTO> getAllRoles() {return roleService.getAllRoles();}
}
@RestController
@RequestMapping("/api/admin/parameters")
@RequiredArgsConstructor
public class ParameterController {private final ParameterService parameterService;@PostMapping@PreAuthorize("hasRole('ADMIN')")public ParameterDTO createParameter(@RequestParam String key,@RequestParam String name,@RequestParam String value,@RequestParam(required = false) String description,@RequestParam(defaultValue = "true") Boolean isEditable) {return parameterService.createParameter(key, name, value, description, isEditable);}@PutMapping("/{key}")@PreAuthorize("hasRole('ADMIN')")public ParameterDTO updateParameter(@PathVariable String key,@RequestParam String value,@RequestParam(required = false) String description) {return parameterService.updateParameter(key, value, description);}@GetMapping("/{key}")public ParameterDTO getParameter(@PathVariable String key) {return parameterService.getParameter(key);}@GetMappingpublic List<ParameterDTO> getAllParameters() {return parameterService.getAllParameters();}
}
http://www.lryc.cn/news/622930.html

相关文章:

  • 小白挑战一周上架元服务——元服务开发06
  • 24. async await 原理是什么,会编译成什么
  • 硬核北京 | 2025世界机器人大会“破圈”,工业智能、康养科技…… 亦庄上演“机器人总动员”
  • 石头科技披露半年报:营收79.03亿元,同比大增78.96%
  • 5 索引的操作
  • 强化学习入门教程(附学习文档)
  • 我的世界Java版1.21.4的Fabric模组开发教程(十九)自定义生物群系
  • 小迪安全v2023学习笔记(六十三讲)—— JS加密断点调试
  • 【图论】分层图 / 拆点
  • 什么是模型预测控制?
  • Windows MCP.Net:革命性的 .NET Windows 桌面自动化 MCP 服务器
  • 【C++学习篇】:基础
  • ZKmall开源商城的数据校验之道:用规范守护业务基石
  • 中本聪思想与Web3的困境:从理论到现实的跨越
  • PyTorch生成式人工智能——使用MusicGen生成音乐
  • 新手向:Python异常处理(try-except-finally)详解
  • JVM垃圾回收器
  • 学习日志35 python
  • Python:如何在Pycharm中显示geemap地图?
  • 基于深度学习的老照片修复系统
  • k8sday08深入控制器(3/3)
  • Docker小游戏 | 使用Docker部署人生重开模拟器
  • K8S的ingress
  • 玩转云原生,使用k9s管理k8s集群和k3s集群
  • 如何在 MacOS 上安装 SQL Server
  • VS Code配置MinGW64编译ALGLIB库
  • 水分含量低、残留物少且紫外光谱纯净的生物溶剂推荐
  • python学习DAY43打卡
  • VScode 使用遇到的问题
  • 北京JAVA基础面试30天打卡11