diff --git a/README.md b/README.md index 3ff9760..5bb689b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,66 @@ # 收支管理系统 -个人收支管理系统,支持收入/支出记录管理、分类管理、月度统计与图表展示。 +个人收支管理系统,支持收入/支出记录管理、分类与标签管理、多来源账单导入、自定义归类规则、AI 智能记账、月度统计与图表展示,并提供定时账单导入提醒与系统日志查看。 + +## 功能特性 + +### 收支记录 +- 收支记录的增删改查、分页与多条件筛选 +- 批量设置分类、批量设置标签、批量删除 +- 单条记录的「计入统计」开关与「备注隐藏」开关 +- 按当前筛选条件导出 Excel + +### 分类与标签 +- 分类、标签的增删改查,新注册用户自动获得一套默认数据 +- 分类图标支持 AI 推荐 Emoji,标签颜色支持 AI 推荐十六进制色值 + +### 统计与图表 +- 月度收支统计、分类占比、收支趋势、分组统计与概览汇总 +- 基于 ECharts 的可视化图表展示 + +### 账单导入 +- 支持支付宝(CSV)、微信(xlsx)、美团、交通银行、招商银行(PDF)账单解析 +- 支持 zip 压缩包(含 ZipCrypto / AES 加密压缩包)上传 +- 「解析预览 → 确认导入」两步流程,按交易号去重 + +### 自定义规则 +- 基于关键词(匹配备注 / 交易对方 / 商品名称)、收支类型、金额区间组合的条件规则 +- 命中后自动应用分类与标签,支持优先级排序与启用 / 停用 +- 账单导入预览阶段展示命中的规则与建议分类 / 标签 + +### AI 能力(DeepSeek) +- 交易记录分类与标签的批量 / 单条智能建议,支持一键采纳 +- 自然语言查询解析与自然语言记账解析 +- AI 生成分类、标签,以及 Emoji / 颜色推荐 +- AI 调用用量统计与明细查看 + +### 定时提醒与通知 +- 按每天 / 每周 / 每月周期提醒导入新账单(默认每月 1 日) +- 多渠道推送:邮箱 SMTP(如 QQ 邮箱)、Server 酱(推送到个人微信) +- 渠道管理、测试发送与发送日志查看 + +### 系统日志 +- 接口访问日志、登录日志的持久化与分页查询 +- 后端运行日志在线查看,支持基于 SSE 的实时推送与筛选 + +### 账号与多语言 +- JWT 登录认证,注册功能可开关 +- 运行时数据库配置与切换(SQLite / MySQL),无需启动前准备 +- 界面支持中文、英文、日文三种语言 ## 技术栈 | 层级 | 技术 | |---|---| -| 后端 | Spring Boot 3.3.6 + Java 21 + MyBatis-Plus + MySQL / SQLite | -| 前端 | Vue 3 + TypeScript + Vite + Naive UI + ECharts | +| 后端 | Spring Boot 3.3.6 + Java 21 + MyBatis-Plus 3.5.12 + MySQL / SQLite | +| 前端 | Vue 3 + TypeScript + Vite + Naive UI + ECharts + Pinia | | 构建 | Gradle 8.13 (后端) / npm (前端) | | 认证 | JWT | +| 对象映射 | MapStruct 1.6.3 | +| 账单解析 | EasyExcel 4.0.3 + PDFBox 3.0.3 + Zip4j 2.11.5 | +| AI 能力 | DeepSeek | +| 通知渠道 | 邮箱 SMTP / Server 酱 | +| 多语言 | 中文 / 英文 / 日文 (vue-i18n) | | API 文档 | Knife4j (OpenAPI 3) | | JDK 版本 | 21.0.12.1 (Temurin LTS) | | Node.js 版本 | v24.0.0 | @@ -78,23 +129,32 @@ java -jar build/libs/finance-app-0.0.1-SNAPSHOT.jar --server.port=18081 finance-app/ ├── build.gradle ├── src/main/java/cn/rosercode/finance/ -│ ├── controller/ # 接口层 -│ ├── service/ # 业务层 -│ ├── mapper/ # 数据访问层 -│ ├── entity/ # 实体类 -│ ├── dto/ # 请求对象 -│ ├── vo/ # 响应对象 -│ ├── config/ # 配置类 +│ ├── client/ # 外部服务客户端(DeepSeek) │ ├── common/ # 公共类 -│ ├── interceptor/ # 拦截器 -│ └── util/ # 工具类 +│ ├── config/ # 配置类(应用配置、动态数据源) +│ ├── controller/ # 接口层 +│ ├── converter/ # MapStruct 对象转换器 +│ ├── dto/ # 请求对象 +│ ├── entity/ # 实体类 +│ ├── enums/ # 枚举 +│ ├── exporter/ # 导出处理 +│ ├── importer/ # 账单解析器(支付宝/微信/美团/银行) +│ ├── interceptor/ # 拦截器(JWT、访问日志) +│ ├── mapper/ # 数据访问层 +│ ├── notification/ # 通知渠道发送器 +│ ├── scheduler/ # 定时任务 +│ ├── service/ # 业务层(含 impl) +│ ├── util/ # 工具类 +│ └── vo/ # 响应对象 ├── src/main/resources/ │ ├── application.yml -│ ├── mapper/ -│ └── sql/init.sql +│ ├── logback-spring.xml +│ ├── mapper/ # MyBatis XML +│ └── sql/ # 建表脚本(init.sql / schema-sqlite.sql 等) └── frontend/ # Vue 3 前端 └── src/ ├── api/ # API 请求封装 + ├── locales/ # 多语言文案 ├── router/ # 路由配置 ├── store/ # 状态管理 └── views/ # 页面组件 diff --git a/build.gradle b/build.gradle index 2f609d1..ba950b7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.6' id 'io.spring.dependency-management' version '1.1.7' + id 'com.diffplug.spotless' version '8.10.2' } group = 'cn.rosercode' @@ -67,6 +68,16 @@ tasks.named('test') { useJUnitPlatform() } +// ===== 代码格式规范 ===== +// 统一 Java 导入顺序:java/javax -> 第三方库 -> 项目包,组间空行分隔 +spotless { + java { + target 'src/main/java/**/*.java', 'src/test/java/**/*.java' + // '|' 表示同组导入不插入空行;'' 为未显式指定导入的兜底组(即所有第三方库) + importOrder('java|javax', '', 'cn.rosercode') + } +} + // ===== 前端构建与打包 ===== def frontendDir = file("${projectDir}/frontend") def frontendDistDir = file("${frontendDir}/dist") diff --git a/settings.gradle b/settings.gradle index a5ef8bb..5b807a6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,13 @@ +pluginManagement { + resolutionStrategy { + eachPlugin { + // Spotless 的插件标记只发布在 Gradle 插件门户,本地 Nexus 未代理; + // 这里改为直接按 Maven 坐标解析插件实现(Maven Central 有该构件) + if (requested.id.id == 'com.diffplug.spotless') { + useModule("com.diffplug.spotless:spotless-plugin-gradle:${requested.version}") + } + } + } +} + rootProject.name = 'finance-app' diff --git a/src/main/java/cn/rosercode/finance/client/DeepSeekClient.java b/src/main/java/cn/rosercode/finance/client/DeepSeekClient.java index b236157..9f24d38 100644 --- a/src/main/java/cn/rosercode/finance/client/DeepSeekClient.java +++ b/src/main/java/cn/rosercode/finance/client/DeepSeekClient.java @@ -1,11 +1,5 @@ package cn.rosercode.finance.client; -import cn.rosercode.finance.common.BusinessException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Component; - import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -17,12 +11,21 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import cn.rosercode.finance.common.BusinessException; + /** * DeepSeek 客户端,封装 Chat Completions 接口的 HTTP 调用与响应解析。 * * @date 2026-09-17 17:52:17 */ @Component +@Slf4j public class DeepSeekClient { /** 单次请求的读取超时时间 */ @@ -63,6 +66,7 @@ public class DeepSeekClient { // 兼容以斜杠结尾的 baseUrl String normalized = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; String url = normalized + "/chat/completions"; + long start = System.currentTimeMillis(); // 构造请求体,强制返回 JSON 对象 Map body = new LinkedHashMap<>(); @@ -79,6 +83,7 @@ public class DeepSeekClient { try { requestBody = objectMapper.writeValueAsString(body); } catch (JsonProcessingException e) { + log.error("构建 AI 请求体失败: model={}, error={}", model, e.getMessage()); throw new BusinessException("构建 AI 请求失败: " + e.getMessage()); } @@ -93,14 +98,18 @@ public class DeepSeekClient { try { response = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); } catch (IOException e) { + log.error("调用 DeepSeek 失败: model={}, error={}", model, e.getMessage()); throw new BusinessException("调用 DeepSeek 失败: " + e.getMessage()); } catch (InterruptedException e) { // 恢复中断标记,避免吞掉中断信号 Thread.currentThread().interrupt(); + log.warn("调用 DeepSeek 被中断: model={}", model); throw new BusinessException("调用 DeepSeek 被中断"); } if (response.statusCode() != 200) { + log.warn("DeepSeek 返回异常状态: model={}, status={}, body={}", + model, response.statusCode(), truncate(response.body())); throw new BusinessException("DeepSeek 返回异常(" + response.statusCode() + "): " + truncate(response.body())); } @@ -119,6 +128,8 @@ public class DeepSeekClient { result.setTotalTokens(usage.path("total_tokens").asInt(0)); // DeepSeek 用 prompt_cache_hit_tokens 表示缓存命中的输入 token result.setCachedTokens(usage.path("prompt_cache_hit_tokens").asInt(0)); + log.info("DeepSeek 调用完成: model={}, 耗时={}ms, tokens={}", model, + System.currentTimeMillis() - start, result.getTotalTokens()); return result; } catch (JsonProcessingException e) { throw new BusinessException("解析 DeepSeek 响应失败: " + e.getMessage()); diff --git a/src/main/java/cn/rosercode/finance/common/GlobalExceptionHandler.java b/src/main/java/cn/rosercode/finance/common/GlobalExceptionHandler.java index de330bc..33af329 100644 --- a/src/main/java/cn/rosercode/finance/common/GlobalExceptionHandler.java +++ b/src/main/java/cn/rosercode/finance/common/GlobalExceptionHandler.java @@ -1,9 +1,11 @@ package cn.rosercode.finance.common; -import cn.rosercode.finance.config.SpaRouteSupport; +import java.io.IOException; + import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -11,13 +13,14 @@ import org.springframework.web.context.request.async.AsyncRequestNotUsableExcept import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.servlet.resource.NoResourceFoundException; -import java.io.IOException; +import cn.rosercode.finance.config.SpaRouteSupport; /** * 全局异常处理器。 *

统一捕获并处理控制器层抛出的各类异常,将其转换为标准化的 JSON 响应返回给前端。

*/ @RestControllerAdvice +@Slf4j public class GlobalExceptionHandler { /** @@ -28,6 +31,7 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(BusinessException.class) public Result handleBusinessException(BusinessException e) { + log.warn("业务异常[{}]: {}", e.getCode(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } @@ -53,6 +57,7 @@ public class GlobalExceptionHandler { public Result handleValidationException(MethodArgumentNotValidException e) { // 提取第一个校验错误信息 String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); + log.warn("参数校验失败: {}", message); return Result.error(400, message); } @@ -108,8 +113,8 @@ public class GlobalExceptionHandler { if (notConfigured != null) { return Result.error(notConfigured.getCode(), notConfigured.getMessage()); } - // 打印异常堆栈信息,便于排查问题 - e.printStackTrace(); + // 记录异常堆栈,便于排查问题 + log.error("服务器内部错误: {}", e.getMessage(), e); return Result.error(500, "服务器内部错误: " + e.getMessage()); } diff --git a/src/main/java/cn/rosercode/finance/common/PageResult.java b/src/main/java/cn/rosercode/finance/common/PageResult.java index 13dc3a9..9397865 100644 --- a/src/main/java/cn/rosercode/finance/common/PageResult.java +++ b/src/main/java/cn/rosercode/finance/common/PageResult.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.common; -import lombok.Data; - import java.util.List; +import lombok.Data; + /** * 分页查询结果封装类。 *

用于封装分页查询的返回结果,包含数据列表、总记录数、当前页码和每页大小。

diff --git a/src/main/java/cn/rosercode/finance/config/Knife4jConfig.java b/src/main/java/cn/rosercode/finance/config/Knife4jConfig.java index 5c13502..835b694 100644 --- a/src/main/java/cn/rosercode/finance/config/Knife4jConfig.java +++ b/src/main/java/cn/rosercode/finance/config/Knife4jConfig.java @@ -1,8 +1,8 @@ package cn.rosercode.finance.config; +import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/cn/rosercode/finance/config/SpaErrorController.java b/src/main/java/cn/rosercode/finance/config/SpaErrorController.java index 8be97d2..227e09b 100644 --- a/src/main/java/cn/rosercode/finance/config/SpaErrorController.java +++ b/src/main/java/cn/rosercode/finance/config/SpaErrorController.java @@ -1,5 +1,9 @@ package cn.rosercode.finance.config; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; @@ -9,10 +13,6 @@ import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; - /** * 前端单页应用路由回退控制器。 * diff --git a/src/main/java/cn/rosercode/finance/config/WebConfig.java b/src/main/java/cn/rosercode/finance/config/WebConfig.java index 9720bda..0e56912 100644 --- a/src/main/java/cn/rosercode/finance/config/WebConfig.java +++ b/src/main/java/cn/rosercode/finance/config/WebConfig.java @@ -1,12 +1,13 @@ package cn.rosercode.finance.config; -import cn.rosercode.finance.interceptor.AccessLogInterceptor; -import cn.rosercode.finance.interceptor.JwtInterceptor; 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; +import cn.rosercode.finance.interceptor.AccessLogInterceptor; +import cn.rosercode.finance.interceptor.JwtInterceptor; + /** * Web MVC 配置类。 *

配置拦截器、跨域策略等 Web 层相关设置。

diff --git a/src/main/java/cn/rosercode/finance/config/app/AppConfigStore.java b/src/main/java/cn/rosercode/finance/config/app/AppConfigStore.java index 503203a..e5735f0 100644 --- a/src/main/java/cn/rosercode/finance/config/app/AppConfigStore.java +++ b/src/main/java/cn/rosercode/finance/config/app/AppConfigStore.java @@ -1,15 +1,14 @@ package cn.rosercode.finance.config.app; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + /** * 应用级运行时配置的本地文件存储。 * @@ -19,10 +18,9 @@ import java.nio.file.Paths; * @date 2026-09-18 12:09:22 */ @Component +@Slf4j public class AppConfigStore { - private static final Logger log = LoggerFactory.getLogger(AppConfigStore.class); - /** 配置目录 */ private static final String CONFIG_DIR = "config"; diff --git a/src/main/java/cn/rosercode/finance/config/datasource/DatabaseBootstrap.java b/src/main/java/cn/rosercode/finance/config/datasource/DatabaseBootstrap.java index 69b76ae..8e83e62 100644 --- a/src/main/java/cn/rosercode/finance/config/datasource/DatabaseBootstrap.java +++ b/src/main/java/cn/rosercode/finance/config/datasource/DatabaseBootstrap.java @@ -1,10 +1,11 @@ package cn.rosercode.finance.config.datasource; -import cn.rosercode.finance.service.DatabaseService; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; +import cn.rosercode.finance.service.DatabaseService; + /** * 数据库启动引导器。 * diff --git a/src/main/java/cn/rosercode/finance/config/datasource/DatabaseConfigStore.java b/src/main/java/cn/rosercode/finance/config/datasource/DatabaseConfigStore.java index 72d05c8..bff60cc 100644 --- a/src/main/java/cn/rosercode/finance/config/datasource/DatabaseConfigStore.java +++ b/src/main/java/cn/rosercode/finance/config/datasource/DatabaseConfigStore.java @@ -1,16 +1,15 @@ package cn.rosercode.finance.config.datasource; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + /** * 数据库配置的本地文件存储。 * @@ -20,10 +19,9 @@ import java.util.Optional; * @date 2026-09-18 11:00:14 */ @Component +@Slf4j public class DatabaseConfigStore { - private static final Logger log = LoggerFactory.getLogger(DatabaseConfigStore.class); - /** 配置文件相对工作目录的位置 */ private static final String CONFIG_DIR = "config"; diff --git a/src/main/java/cn/rosercode/finance/config/datasource/DynamicDataSource.java b/src/main/java/cn/rosercode/finance/config/datasource/DynamicDataSource.java index a76b7e8..06be44c 100644 --- a/src/main/java/cn/rosercode/finance/config/datasource/DynamicDataSource.java +++ b/src/main/java/cn/rosercode/finance/config/datasource/DynamicDataSource.java @@ -1,13 +1,12 @@ package cn.rosercode.finance.config.datasource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; - -import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.sql.DataSource; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 动态数据源,支持在运行时切换底层真实数据源。 @@ -19,10 +18,9 @@ import java.util.concurrent.ConcurrentHashMap; * * @date 2026-09-18 11:00:28 */ +@Slf4j public class DynamicDataSource extends AbstractRoutingDataSource { - private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class); - /** 自行维护的目标数据源映射,用于运行时增量注册与切换 */ private final Map targetDataSources = new ConcurrentHashMap<>(); diff --git a/src/main/java/cn/rosercode/finance/config/datasource/SchemaInitializer.java b/src/main/java/cn/rosercode/finance/config/datasource/SchemaInitializer.java index 9a2234b..83c0b7b 100644 --- a/src/main/java/cn/rosercode/finance/config/datasource/SchemaInitializer.java +++ b/src/main/java/cn/rosercode/finance/config/datasource/SchemaInitializer.java @@ -1,13 +1,12 @@ package cn.rosercode.finance.config.datasource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import javax.sql.DataSource; + +import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.stereotype.Component; -import javax.sql.DataSource; - /** * 数据库表结构初始化器。 * @@ -17,10 +16,9 @@ import javax.sql.DataSource; * @date 2026-09-18 11:00:46 */ @Component +@Slf4j public class SchemaInitializer { - private static final Logger log = LoggerFactory.getLogger(SchemaInitializer.class); - /** MySQL 建表脚本 */ private static final String MYSQL_SCRIPT = "sql/init.sql"; diff --git a/src/main/java/cn/rosercode/finance/config/datasource/UnconfiguredDataSource.java b/src/main/java/cn/rosercode/finance/config/datasource/UnconfiguredDataSource.java index 87b9f70..96c9a57 100644 --- a/src/main/java/cn/rosercode/finance/config/datasource/UnconfiguredDataSource.java +++ b/src/main/java/cn/rosercode/finance/config/datasource/UnconfiguredDataSource.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.config.datasource; -import cn.rosercode.finance.common.DatabaseNotConfiguredException; - -import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.util.logging.Logger; +import javax.sql.DataSource; + +import cn.rosercode.finance.common.DatabaseNotConfiguredException; /** * 未配置数据库时使用的占位数据源。 diff --git a/src/main/java/cn/rosercode/finance/controller/AiController.java b/src/main/java/cn/rosercode/finance/controller/AiController.java index d54819b..835d2d8 100644 --- a/src/main/java/cn/rosercode/finance/controller/AiController.java +++ b/src/main/java/cn/rosercode/finance/controller/AiController.java @@ -1,5 +1,18 @@ package cn.rosercode.finance.controller; +import java.util.List; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + import cn.rosercode.finance.common.Result; import cn.rosercode.finance.dto.AiApplyRequest; import cn.rosercode.finance.dto.AiCategoryRequest; @@ -18,6 +31,7 @@ import cn.rosercode.finance.service.AiRecordService; import cn.rosercode.finance.service.AiSuggestionService; import cn.rosercode.finance.service.AiTagService; import cn.rosercode.finance.service.AiUsageService; +import cn.rosercode.finance.util.UserContext; import cn.rosercode.finance.vo.AiCategoryResultVO; import cn.rosercode.finance.vo.AiConfigVO; import cn.rosercode.finance.vo.AiQueryResultVO; @@ -25,19 +39,6 @@ import cn.rosercode.finance.vo.AiRecordResultVO; import cn.rosercode.finance.vo.AiSuggestionVO; import cn.rosercode.finance.vo.AiTagResultVO; import cn.rosercode.finance.vo.AiUsageResultVO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.validation.Valid; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; /** * AI 控制器 @@ -77,27 +78,25 @@ public class AiController { /** * 获取当前用户的 AI 配置 * - * @param request HTTP请求,从中获取用户ID * @return AI 配置(API Key 脱敏) */ @GetMapping("/config") @Operation(summary = "获取 AI 配置", description = "获取当前用户的 AI 智能建议配置,API Key 已脱敏") - public Result getConfig(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result getConfig() { + Long userId = UserContext.getUserId(); return Result.success(aiConfigService.getConfig(userId)); } /** * 保存当前用户的 AI 配置 * - * @param request HTTP请求,从中获取用户ID * @param configRequest 配置保存请求 * @return 操作结果 */ @PutMapping("/config") @Operation(summary = "保存 AI 配置", description = "更新当前用户的 AI 配置,API Key 留空表示不修改") - public Result saveConfig(HttpServletRequest request, @RequestBody AiConfigRequest configRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result saveConfig(@RequestBody AiConfigRequest configRequest) { + Long userId = UserContext.getUserId(); aiConfigService.saveConfig(userId, configRequest); return Result.success(); } @@ -105,13 +104,12 @@ public class AiController { /** * 测试 DeepSeek 连通性 * - * @param request HTTP请求,从中获取用户ID * @return 操作结果 */ @PostMapping("/config/test") @Operation(summary = "测试连接", description = "使用当前配置向 DeepSeek 发送一条测试请求") - public Result testConfig(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result testConfig() { + Long userId = UserContext.getUserId(); aiConfigService.testConnection(userId); return Result.success(); } @@ -119,150 +117,130 @@ public class AiController { /** * 生成分类与标签建议 * - * @param request HTTP请求,从中获取用户ID * @param suggestRequest 建议请求,携带记录ID列表 * @return 逐条建议列表 */ @PostMapping("/suggest") @Operation(summary = "生成 AI 建议", description = "根据交易记录内容生成分类与标签建议") - public Result> suggest(HttpServletRequest request, - @RequestBody @Valid AiSuggestRequest suggestRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result> suggest(@RequestBody @Valid AiSuggestRequest suggestRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiSuggestionService.suggest(userId, suggestRequest.getIds(), suggestRequest.getScope())); } /** * 根据表单当前内容推荐分类与标签 * - * @param request HTTP请求,从中获取用户ID * @param suggestOneRequest 建议请求,携带表单当前内容 * @return 单条建议结果 */ @PostMapping("/suggest-one") @Operation(summary = "单条记录 AI 建议", description = "根据表单当前内容推荐分类与标签,供新增或编辑时参考") - public Result suggestOne(HttpServletRequest request, - @RequestBody @Valid AiSuggestOneRequest suggestOneRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result suggestOne(@RequestBody @Valid AiSuggestOneRequest suggestOneRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiSuggestionService.suggestOne(userId, suggestOneRequest)); } /** * 一键应用 AI 建议 * - * @param request HTTP请求,从中获取用户ID * @param applyRequest 应用请求,携带逐条建议 * @return 实际发生变更的记录数 */ @PostMapping("/apply") @Operation(summary = "应用 AI 建议", description = "覆盖分类并追加标签,一键写入建议") - public Result apply(HttpServletRequest request, - @RequestBody @Valid AiApplyRequest applyRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result apply(@RequestBody @Valid AiApplyRequest applyRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiSuggestionService.apply(userId, applyRequest)); } /** * 获取 AI 使用情况 * - * @param request HTTP请求,从中获取用户ID * @param limit 最近明细条数,默认 20 * @return 累计汇总与最近调用明细 */ @GetMapping("/usage") @Operation(summary = "获取 AI 使用情况", description = "获取当前用户 AI 调用的累计汇总与最近调用明细") - public Result usage(HttpServletRequest request, - @RequestParam(defaultValue = "20") int limit) { - Long userId = (Long) request.getAttribute("userId"); + public Result usage(@RequestParam(defaultValue = "20") int limit) { + Long userId = UserContext.getUserId(); return Result.success(aiUsageService.getUsage(userId, limit)); } /** * 解析自然语言查询为筛选条件 * - * @param request HTTP请求,从中获取用户ID * @param queryRequest 查询请求,携带自然语言语句 * @return 结构化筛选条件 */ @PostMapping("/query") @Operation(summary = "自然语言查询解析", description = "将自然语言查询解析为交易筛选条件,供用户确认后应用") - public Result query(HttpServletRequest request, - @RequestBody @Valid AiQueryRequest queryRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result query(@RequestBody @Valid AiQueryRequest queryRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiQueryService.parse(userId, queryRequest.getQuery())); } /** * 解析自然语言记账内容为交易记录 * - * @param request HTTP请求,从中获取用户ID * @param recordRequest 记账请求,携带自然语言语句 * @return 结构化交易记录 */ @PostMapping("/record") @Operation(summary = "自然语言记账解析", description = "将自然语言记账内容解析为交易记录,供用户确认后保存") - public Result record(HttpServletRequest request, - @RequestBody @Valid AiRecordRequest recordRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result record(@RequestBody @Valid AiRecordRequest recordRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiRecordService.parse(userId, recordRequest.getText())); } /** * 根据自然语言描述生成分类 * - * @param request HTTP请求,从中获取用户ID * @param categoryRequest 生成请求,携带分类用途描述 * @return 分类建议 */ @PostMapping("/category") @Operation(summary = "AI 生成分类", description = "根据自然语言描述生成分类名称、类型与图标") - public Result generateCategory(HttpServletRequest request, - @RequestBody @Valid AiCategoryRequest categoryRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result generateCategory(@RequestBody @Valid AiCategoryRequest categoryRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiCategoryService.parse(userId, categoryRequest.getText())); } /** * 根据自然语言描述生成标签 * - * @param request HTTP请求,从中获取用户ID * @param tagRequest 生成请求,携带标签用途描述 * @return 标签建议 */ @PostMapping("/tag") @Operation(summary = "AI 生成标签", description = "根据自然语言描述生成标签名称与颜色") - public Result generateTag(HttpServletRequest request, - @RequestBody @Valid AiTagRequest tagRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result generateTag(@RequestBody @Valid AiTagRequest tagRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiTagService.parse(userId, tagRequest.getText())); } /** * 根据分类名称推荐 Emoji 图标 * - * @param request HTTP请求,从中获取用户ID * @param decorateRequest 建议请求,携带分类名称与收支类型 * @return 推荐的 Emoji */ @PostMapping("/emoji") @Operation(summary = "AI 推荐分类图标", description = "根据分类名称与收支类型推荐一个 Emoji") - public Result suggestEmoji(HttpServletRequest request, - @RequestBody @Valid AiDecorateRequest decorateRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result suggestEmoji(@RequestBody @Valid AiDecorateRequest decorateRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiDecorationService.suggestEmoji(userId, decorateRequest.getName(), decorateRequest.getType())); } /** * 根据标签名称推荐颜色 * - * @param request HTTP请求,从中获取用户ID * @param decorateRequest 建议请求,携带标签名称 * @return 推荐的十六进制颜色 */ @PostMapping("/color") @Operation(summary = "AI 推荐标签颜色", description = "根据标签名称推荐一个十六进制颜色") - public Result suggestColor(HttpServletRequest request, - @RequestBody @Valid AiDecorateRequest decorateRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result suggestColor(@RequestBody @Valid AiDecorateRequest decorateRequest) { + Long userId = UserContext.getUserId(); return Result.success(aiDecorationService.suggestColor(userId, decorateRequest.getName())); } } diff --git a/src/main/java/cn/rosercode/finance/controller/AppConfigController.java b/src/main/java/cn/rosercode/finance/controller/AppConfigController.java index a6c131f..a99f1ed 100644 --- a/src/main/java/cn/rosercode/finance/controller/AppConfigController.java +++ b/src/main/java/cn/rosercode/finance/controller/AppConfigController.java @@ -1,10 +1,5 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.config.app.AppConfig; -import cn.rosercode.finance.config.app.AppConfigStore; -import cn.rosercode.finance.dto.LogRetentionRequest; -import cn.rosercode.finance.dto.NotificationUrlRequest; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.GetMapping; @@ -13,6 +8,12 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.config.app.AppConfig; +import cn.rosercode.finance.config.app.AppConfigStore; +import cn.rosercode.finance.dto.LogRetentionRequest; +import cn.rosercode.finance.dto.NotificationUrlRequest; + /** * 应用配置控制器 * diff --git a/src/main/java/cn/rosercode/finance/controller/BillImportController.java b/src/main/java/cn/rosercode/finance/controller/BillImportController.java index 04b1573..19c2fd2 100644 --- a/src/main/java/cn/rosercode/finance/controller/BillImportController.java +++ b/src/main/java/cn/rosercode/finance/controller/BillImportController.java @@ -1,16 +1,10 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.BusinessException; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.BillImportRequest; -import cn.rosercode.finance.enums.BillType; -import cn.rosercode.finance.service.BillImportService; -import cn.rosercode.finance.vo.BillImportResultVO; -import cn.rosercode.finance.vo.BillParseResultVO; +import java.io.IOException; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -19,7 +13,14 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; +import cn.rosercode.finance.common.BusinessException; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.BillImportRequest; +import cn.rosercode.finance.enums.BillType; +import cn.rosercode.finance.service.BillImportService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.BillImportResultVO; +import cn.rosercode.finance.vo.BillParseResultVO; /** * 账单导入控制器 @@ -42,7 +43,6 @@ public class BillImportController { /** * 解析账单文件(不落库) * - * @param request HTTP请求,从中获取用户ID * @param file 账单文件(支持 csv/xlsx 或 zip 压缩包) * @param billType 账单类型,如 ALIPAY * @param password 压缩包解压密码,未加密时可不传 @@ -51,11 +51,10 @@ public class BillImportController { */ @PostMapping("/parse") @Operation(summary = "解析账单文件", description = "解析账单文件(支持 zip 压缩包)并返回预览数据,不写入数据库") - public Result parse(HttpServletRequest request, - @Parameter(description = "账单文件") @RequestParam("file") MultipartFile file, + public Result parse(@Parameter(description = "账单文件") @RequestParam("file") MultipartFile file, @Parameter(description = "账单类型,如 ALIPAY") @RequestParam("billType") String billType, @Parameter(description = "压缩包解压密码") @RequestParam(value = "password", required = false) String password) throws IOException { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); if (file == null || file.isEmpty()) { throw new BusinessException("请选择账单文件"); } @@ -65,15 +64,13 @@ public class BillImportController { /** * 确认导入用户勾选的账单行 * - * @param request HTTP请求,从中获取用户ID - * @param body 导入请求(默认分类与勾选行) + * @param body 导入请求(默认分类与勾选行) * @return 导入结果统计 */ @PostMapping("/confirm") @Operation(summary = "确认导入账单", description = "将用户勾选的账单行写入交易记录,按交易号去重") - public Result confirm(HttpServletRequest request, - @RequestBody @Valid BillImportRequest body) { - Long userId = (Long) request.getAttribute("userId"); + public Result confirm(@RequestBody @Valid BillImportRequest body) { + Long userId = UserContext.getUserId(); return Result.success(billImportService.confirm(userId, body)); } diff --git a/src/main/java/cn/rosercode/finance/controller/CategoryController.java b/src/main/java/cn/rosercode/finance/controller/CategoryController.java index 05e71c1..f78c3cb 100644 --- a/src/main/java/cn/rosercode/finance/controller/CategoryController.java +++ b/src/main/java/cn/rosercode/finance/controller/CategoryController.java @@ -1,13 +1,10 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.CategoryRequest; -import cn.rosercode.finance.service.CategoryService; -import cn.rosercode.finance.vo.CategoryVO; +import java.util.List; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -19,7 +16,11 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.CategoryRequest; +import cn.rosercode.finance.service.CategoryService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.CategoryVO; /** * 分类控制器 @@ -40,15 +41,13 @@ public class CategoryController { /** * 获取分类列表 * - * @param request HTTP请求,从中获取用户ID * @param type 分类类型(可选),用于按类型筛选 * @return 分类列表 */ @GetMapping("/list") @Operation(summary = "获取分类列表", description = "获取当前用户的全部分类,支持按类型筛选") - public Result> list(HttpServletRequest request, - @Parameter(description = "分类类型,可选值:income/expense") @RequestParam(required = false) String type) { - Long userId = (Long) request.getAttribute("userId"); + public Result> list(@Parameter(description = "分类类型,可选值:income/expense") @RequestParam(required = false) String type) { + Long userId = UserContext.getUserId(); if (type != null && !type.isEmpty()) { return Result.success(categoryService.listByType(userId, type)); } @@ -58,14 +57,13 @@ public class CategoryController { /** * 创建分类 * - * @param request HTTP请求,从中获取用户ID * @param categoryRequest 分类请求参数 * @return 操作结果 */ @PostMapping @Operation(summary = "创建分类", description = "为当前用户创建新的收支分类") - public Result create(HttpServletRequest request, @RequestBody @Valid CategoryRequest categoryRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result create(@RequestBody @Valid CategoryRequest categoryRequest) { + Long userId = UserContext.getUserId(); categoryService.create(userId, categoryRequest); return Result.success(); } @@ -73,17 +71,15 @@ public class CategoryController { /** * 更新分类 * - * @param request HTTP请求,从中获取用户ID * @param id 分类ID * @param categoryRequest 分类请求参数 * @return 操作结果 */ @PutMapping("/{id}") @Operation(summary = "更新分类", description = "根据分类ID更新分类信息") - public Result update(HttpServletRequest request, - @Parameter(description = "分类ID") @PathVariable Long id, + public Result update(@Parameter(description = "分类ID") @PathVariable Long id, @RequestBody @Valid CategoryRequest categoryRequest) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); categoryService.update(userId, id, categoryRequest); return Result.success(); } @@ -91,15 +87,13 @@ public class CategoryController { /** * 删除分类 * - * @param request HTTP请求,从中获取用户ID * @param id 分类ID * @return 操作结果 */ @DeleteMapping("/{id}") @Operation(summary = "删除分类", description = "根据分类ID删除指定分类") - public Result delete(HttpServletRequest request, - @Parameter(description = "分类ID") @PathVariable Long id) { - Long userId = (Long) request.getAttribute("userId"); + public Result delete(@Parameter(description = "分类ID") @PathVariable Long id) { + Long userId = UserContext.getUserId(); categoryService.delete(userId, id); return Result.success(); } diff --git a/src/main/java/cn/rosercode/finance/controller/DatabaseController.java b/src/main/java/cn/rosercode/finance/controller/DatabaseController.java index f4680d8..13d5954 100644 --- a/src/main/java/cn/rosercode/finance/controller/DatabaseController.java +++ b/src/main/java/cn/rosercode/finance/controller/DatabaseController.java @@ -1,15 +1,16 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.DatabaseConfigRequest; -import cn.rosercode.finance.service.DatabaseService; -import cn.rosercode.finance.vo.DatabaseStatusVO; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.DatabaseConfigRequest; +import cn.rosercode.finance.service.DatabaseService; +import cn.rosercode.finance.vo.DatabaseStatusVO; + /** * 数据库配置控制器。 * diff --git a/src/main/java/cn/rosercode/finance/controller/LogController.java b/src/main/java/cn/rosercode/finance/controller/LogController.java index e372576..417ec29 100644 --- a/src/main/java/cn/rosercode/finance/controller/LogController.java +++ b/src/main/java/cn/rosercode/finance/controller/LogController.java @@ -1,5 +1,13 @@ package cn.rosercode.finance.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + import cn.rosercode.finance.common.PageResult; import cn.rosercode.finance.common.Result; import cn.rosercode.finance.dto.LogQuery; @@ -8,13 +16,6 @@ import cn.rosercode.finance.service.LogService; import cn.rosercode.finance.vo.AppLogVO; import cn.rosercode.finance.vo.HttpLogVO; import cn.rosercode.finance.vo.LoginLogVO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; /** * 系统日志控制器 diff --git a/src/main/java/cn/rosercode/finance/controller/NotificationController.java b/src/main/java/cn/rosercode/finance/controller/NotificationController.java index c5b6824..8da9c46 100644 --- a/src/main/java/cn/rosercode/finance/controller/NotificationController.java +++ b/src/main/java/cn/rosercode/finance/controller/NotificationController.java @@ -1,17 +1,9 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.NotificationChannelRequest; -import cn.rosercode.finance.dto.NotificationConfigRequest; -import cn.rosercode.finance.service.NotificationChannelService; -import cn.rosercode.finance.service.NotificationConfigService; -import cn.rosercode.finance.service.NotificationService; -import cn.rosercode.finance.vo.NotificationChannelVO; -import cn.rosercode.finance.vo.NotificationConfigVO; -import cn.rosercode.finance.vo.NotificationLogVO; +import java.util.List; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -22,7 +14,16 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.NotificationChannelRequest; +import cn.rosercode.finance.dto.NotificationConfigRequest; +import cn.rosercode.finance.service.NotificationChannelService; +import cn.rosercode.finance.service.NotificationConfigService; +import cn.rosercode.finance.service.NotificationService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.NotificationChannelVO; +import cn.rosercode.finance.vo.NotificationConfigVO; +import cn.rosercode.finance.vo.NotificationLogVO; /** * 通知控制器 @@ -51,28 +52,25 @@ public class NotificationController { /** * 获取当前用户的通知配置。 * - * @param request HTTP请求,从中获取用户ID * @return 通知配置 */ @GetMapping("/config") @Operation(summary = "获取通知配置", description = "获取当前用户的账单导入提醒周期配置") - public Result getConfig(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result getConfig() { + Long userId = UserContext.getUserId(); return Result.success(notificationConfigService.getConfig(userId)); } /** * 保存当前用户的通知配置。 * - * @param request HTTP请求,从中获取用户ID * @param configRequest 配置保存请求 * @return 操作结果 */ @PutMapping("/config") @Operation(summary = "保存通知配置", description = "更新当前用户的提醒周期与发送时间") - public Result saveConfig(HttpServletRequest request, - @RequestBody NotificationConfigRequest configRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result saveConfig(@RequestBody NotificationConfigRequest configRequest) { + Long userId = UserContext.getUserId(); notificationConfigService.saveConfig(userId, configRequest); return Result.success(); } @@ -80,44 +78,40 @@ public class NotificationController { /** * 查询当前用户的通知渠道。 * - * @param request HTTP请求,从中获取用户ID * @return 渠道列表 */ @GetMapping("/channels") @Operation(summary = "查询通知渠道", description = "查询当前用户配置的所有推送渠道") - public Result> listChannels(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result> listChannels() { + Long userId = UserContext.getUserId(); return Result.success(notificationChannelService.listChannels(userId)); } /** * 新增通知渠道。 * - * @param request HTTP请求,从中获取用户ID * @param channelRequest 渠道保存请求 * @return 新渠道ID */ @PostMapping("/channels") @Operation(summary = "新增通知渠道", description = "新增邮箱或 Server 酱推送渠道") - public Result createChannel(HttpServletRequest request, - @RequestBody NotificationChannelRequest channelRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result createChannel(@RequestBody NotificationChannelRequest channelRequest) { + Long userId = UserContext.getUserId(); return Result.success(notificationChannelService.createChannel(userId, channelRequest)); } /** * 修改通知渠道。 * - * @param request HTTP请求,从中获取用户ID * @param id 渠道ID * @param channelRequest 渠道保存请求 * @return 操作结果 */ @PutMapping("/channels/{id}") @Operation(summary = "修改通知渠道", description = "更新指定渠道的名称、开关与配置") - public Result updateChannel(HttpServletRequest request, @PathVariable Long id, + public Result updateChannel(@PathVariable Long id, @RequestBody NotificationChannelRequest channelRequest) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); notificationChannelService.updateChannel(userId, id, channelRequest); return Result.success(); } @@ -125,14 +119,13 @@ public class NotificationController { /** * 删除通知渠道。 * - * @param request HTTP请求,从中获取用户ID - * @param id 渠道ID + * @param id 渠道ID * @return 操作结果 */ @DeleteMapping("/channels/{id}") @Operation(summary = "删除通知渠道", description = "删除指定的推送渠道") - public Result deleteChannel(HttpServletRequest request, @PathVariable Long id) { - Long userId = (Long) request.getAttribute("userId"); + public Result deleteChannel(@PathVariable Long id) { + Long userId = UserContext.getUserId(); notificationChannelService.deleteChannel(userId, id); return Result.success(); } @@ -140,14 +133,13 @@ public class NotificationController { /** * 测试指定渠道。 * - * @param request HTTP请求,从中获取用户ID - * @param id 渠道ID + * @param id 渠道ID * @return 操作结果 */ @PostMapping("/channels/{id}/test") @Operation(summary = "测试通知渠道", description = "向指定渠道发送一条测试消息") - public Result testChannel(HttpServletRequest request, @PathVariable Long id) { - Long userId = (Long) request.getAttribute("userId"); + public Result testChannel(@PathVariable Long id) { + Long userId = UserContext.getUserId(); notificationService.sendTest(userId, id); return Result.success(); } @@ -155,28 +147,25 @@ public class NotificationController { /** * 立即发送账单导入提醒。 * - * @param request HTTP请求,从中获取用户ID * @return 发送成功的渠道数 */ @PostMapping("/send-now") @Operation(summary = "立即发送提醒", description = "跳过周期设置,立即向所有已启用渠道发送账单导入提醒") - public Result sendNow(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result sendNow() { + Long userId = UserContext.getUserId(); return Result.success(notificationService.sendReminderNow(userId)); } /** * 查询最近的发送日志。 * - * @param request HTTP请求,从中获取用户ID - * @param limit 条数上限,默认 20 + * @param limit 条数上限,默认 20 * @return 日志列表 */ @GetMapping("/logs") @Operation(summary = "查询发送日志", description = "查询当前用户最近的提醒发送结果") - public Result> listLogs(HttpServletRequest request, - @RequestParam(defaultValue = "20") int limit) { - Long userId = (Long) request.getAttribute("userId"); + public Result> listLogs(@RequestParam(defaultValue = "20") int limit) { + Long userId = UserContext.getUserId(); return Result.success(notificationService.listLogs(userId, limit)); } } diff --git a/src/main/java/cn/rosercode/finance/controller/RuleController.java b/src/main/java/cn/rosercode/finance/controller/RuleController.java index 9bb2204..7a3d344 100644 --- a/src/main/java/cn/rosercode/finance/controller/RuleController.java +++ b/src/main/java/cn/rosercode/finance/controller/RuleController.java @@ -1,13 +1,10 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.RuleRequest; -import cn.rosercode.finance.service.RuleService; -import cn.rosercode.finance.vo.RuleVO; +import java.util.List; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.Data; import org.springframework.web.bind.annotation.DeleteMapping; @@ -19,7 +16,11 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.RuleRequest; +import cn.rosercode.finance.service.RuleService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.RuleVO; /** * 规则控制器 @@ -42,27 +43,25 @@ public class RuleController { /** * 获取规则列表 * - * @param request HTTP请求,从中获取用户ID * @return 规则列表 */ @GetMapping("/list") @Operation(summary = "获取规则列表", description = "获取当前用户的全部规则,按优先级排序") - public Result> list(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result> list() { + Long userId = UserContext.getUserId(); return Result.success(ruleService.list(userId)); } /** * 创建规则 * - * @param request HTTP请求,从中获取用户ID * @param ruleRequest 规则请求参数 * @return 操作结果 */ @PostMapping @Operation(summary = "创建规则", description = "为当前用户创建新的分类与标签规则") - public Result create(HttpServletRequest request, @RequestBody @Valid RuleRequest ruleRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result create(@RequestBody @Valid RuleRequest ruleRequest) { + Long userId = UserContext.getUserId(); ruleService.create(userId, ruleRequest); return Result.success(); } @@ -70,17 +69,15 @@ public class RuleController { /** * 更新规则 * - * @param request HTTP请求,从中获取用户ID * @param id 规则ID * @param ruleRequest 规则请求参数 * @return 操作结果 */ @PutMapping("/{id}") @Operation(summary = "更新规则", description = "根据规则ID更新规则信息") - public Result update(HttpServletRequest request, - @Parameter(description = "规则ID") @PathVariable Long id, + public Result update(@Parameter(description = "规则ID") @PathVariable Long id, @RequestBody @Valid RuleRequest ruleRequest) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); ruleService.update(userId, id, ruleRequest); return Result.success(); } @@ -88,15 +85,13 @@ public class RuleController { /** * 删除规则 * - * @param request HTTP请求,从中获取用户ID - * @param id 规则ID + * @param id 规则ID * @return 操作结果 */ @DeleteMapping("/{id}") @Operation(summary = "删除规则", description = "根据规则ID删除指定规则及其标签关联") - public Result delete(HttpServletRequest request, - @Parameter(description = "规则ID") @PathVariable Long id) { - Long userId = (Long) request.getAttribute("userId"); + public Result delete(@Parameter(description = "规则ID") @PathVariable Long id) { + Long userId = UserContext.getUserId(); ruleService.delete(userId, id); return Result.success(); } @@ -104,17 +99,15 @@ public class RuleController { /** * 启用或停用规则 * - * @param request HTTP请求,从中获取用户ID * @param id 规则ID * @param enabled 是否启用 * @return 操作结果 */ @PutMapping("/{id}/enabled") @Operation(summary = "启用/停用规则", description = "切换指定规则的启用状态") - public Result toggleEnabled(HttpServletRequest request, - @Parameter(description = "规则ID") @PathVariable Long id, + public Result toggleEnabled(@Parameter(description = "规则ID") @PathVariable Long id, @RequestBody RuleEnabledRequest enabled) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); ruleService.toggleEnabled(userId, id, enabled.getEnabled()); return Result.success(); } diff --git a/src/main/java/cn/rosercode/finance/controller/StatisticsController.java b/src/main/java/cn/rosercode/finance/controller/StatisticsController.java index ce8e2b0..c7b7f22 100644 --- a/src/main/java/cn/rosercode/finance/controller/StatisticsController.java +++ b/src/main/java/cn/rosercode/finance/controller/StatisticsController.java @@ -1,26 +1,27 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.StatisticsQuery; -import cn.rosercode.finance.dto.StatisticsRangeQuery; -import cn.rosercode.finance.enums.StatisticsDimension; -import cn.rosercode.finance.service.StatisticsService; -import cn.rosercode.finance.vo.CategoryStatisticsVO; -import cn.rosercode.finance.vo.MonthlyStatisticsVO; -import cn.rosercode.finance.vo.StatisticsGroupVO; -import cn.rosercode.finance.vo.StatisticsSummaryVO; -import cn.rosercode.finance.vo.TrendStatisticsVO; +import java.util.List; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.StatisticsQuery; +import cn.rosercode.finance.dto.StatisticsRangeQuery; +import cn.rosercode.finance.enums.StatisticsDimension; +import cn.rosercode.finance.service.StatisticsService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.CategoryStatisticsVO; +import cn.rosercode.finance.vo.MonthlyStatisticsVO; +import cn.rosercode.finance.vo.StatisticsGroupVO; +import cn.rosercode.finance.vo.StatisticsSummaryVO; +import cn.rosercode.finance.vo.TrendStatisticsVO; /** * 统计控制器 @@ -41,77 +42,70 @@ public class StatisticsController { /** * 获取月度统计数据 * - * @param request HTTP请求,从中获取用户ID * @param query 统计查询参数 * @return 月度统计结果 */ @GetMapping("/monthly") @Operation(summary = "获取月度统计数据", description = "获取当前用户指定月份的收支统计汇总数据") - public Result monthly(HttpServletRequest request, @Valid + public Result monthly(@Valid @Parameter(description = "统计查询参数") StatisticsQuery query) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); return Result.success(statisticsService.getMonthlyStatistics(userId, query)); } /** * 获取分类统计数据 * - * @param request HTTP请求,从中获取用户ID * @param query 统计查询参数 * @return 分类统计列表 */ @GetMapping("/category") @Operation(summary = "获取分类统计数据", description = "获取当前用户指定月份按分类维度的统计数据") - public Result> category(HttpServletRequest request, @Valid + public Result> category(@Valid @Parameter(description = "统计查询参数") StatisticsQuery query) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); return Result.success(statisticsService.getCategoryStatistics(userId, query)); } /** * 获取趋势统计数据 * - * @param request HTTP请求,从中获取用户ID * @param query 统计查询参数 * @return 趋势统计列表 */ @GetMapping("/trend") @Operation(summary = "获取趋势统计数据", description = "获取当前用户指定时间范围内的收支趋势数据") - public Result> trend(HttpServletRequest request, @Valid + public Result> trend(@Valid @Parameter(description = "统计查询参数") StatisticsQuery query) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); return Result.success(statisticsService.getTrendStatistics(userId, query)); } /** * 获取指定时间范围的统计概览 * - * @param request HTTP请求,从中获取用户ID - * @param query 时间范围查询参数 + * @param query 时间范围查询参数 * @return 统计概览 */ @GetMapping("/summary") @Operation(summary = "获取统计概览", description = "获取当前用户指定时间范围的收支汇总、日均、最大单笔与平均每笔") - public Result summary(HttpServletRequest request, - @Parameter(description = "时间范围查询参数") StatisticsRangeQuery query) { - Long userId = (Long) request.getAttribute("userId"); + public Result summary(@Parameter(description = "时间范围查询参数") StatisticsRangeQuery query) { + Long userId = UserContext.getUserId(); return Result.success(statisticsService.getSummary(userId, query)); } /** * 按维度获取分组统计数据 * - * @param request HTTP请求,从中获取用户ID * @param dimension 统计维度(day/week/month/quarter/year/paymentMethod/category/type) * @param query 时间范围查询参数 * @return 分组统计列表 */ @GetMapping("/group") @Operation(summary = "获取分组统计数据", description = "按指定维度对时间范围内的交易进行分组统计") - public Result> group(HttpServletRequest request, - @Parameter(description = "统计维度") @RequestParam("dimension") String dimension, + public Result> group(@Parameter(description = "统计维度") @RequestParam("dimension") String dimension, @Parameter(description = "时间范围查询参数") StatisticsRangeQuery query) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); return Result.success(statisticsService.getGroup(userId, query, StatisticsDimension.from(dimension))); } } diff --git a/src/main/java/cn/rosercode/finance/controller/TagController.java b/src/main/java/cn/rosercode/finance/controller/TagController.java index e0eed52..f8f4cb5 100644 --- a/src/main/java/cn/rosercode/finance/controller/TagController.java +++ b/src/main/java/cn/rosercode/finance/controller/TagController.java @@ -1,13 +1,10 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.TagRequest; -import cn.rosercode.finance.service.TagService; -import cn.rosercode.finance.vo.TagVO; +import java.util.List; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -18,7 +15,11 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.TagRequest; +import cn.rosercode.finance.service.TagService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.TagVO; /** * 标签控制器 @@ -41,27 +42,25 @@ public class TagController { /** * 获取标签列表 * - * @param request HTTP请求,从中获取用户ID * @return 标签列表 */ @GetMapping("/list") @Operation(summary = "获取标签列表", description = "获取当前用户的全部标签") - public Result> list(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result> list() { + Long userId = UserContext.getUserId(); return Result.success(tagService.list(userId)); } /** * 创建标签 * - * @param request HTTP请求,从中获取用户ID * @param tagRequest 标签请求参数 * @return 操作结果 */ @PostMapping @Operation(summary = "创建标签", description = "为当前用户创建新的标签") - public Result create(HttpServletRequest request, @RequestBody @Valid TagRequest tagRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result create(@RequestBody @Valid TagRequest tagRequest) { + Long userId = UserContext.getUserId(); tagService.create(userId, tagRequest); return Result.success(); } @@ -69,17 +68,15 @@ public class TagController { /** * 更新标签 * - * @param request HTTP请求,从中获取用户ID * @param id 标签ID * @param tagRequest 标签请求参数 * @return 操作结果 */ @PutMapping("/{id}") @Operation(summary = "更新标签", description = "根据标签ID更新标签信息") - public Result update(HttpServletRequest request, - @Parameter(description = "标签ID") @PathVariable Long id, + public Result update(@Parameter(description = "标签ID") @PathVariable Long id, @RequestBody @Valid TagRequest tagRequest) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); tagService.update(userId, id, tagRequest); return Result.success(); } @@ -87,15 +84,13 @@ public class TagController { /** * 删除标签 * - * @param request HTTP请求,从中获取用户ID - * @param id 标签ID + * @param id 标签ID * @return 操作结果 */ @DeleteMapping("/{id}") @Operation(summary = "删除标签", description = "根据标签ID删除指定标签及其交易关联") - public Result delete(HttpServletRequest request, - @Parameter(description = "标签ID") @PathVariable Long id) { - Long userId = (Long) request.getAttribute("userId"); + public Result delete(@Parameter(description = "标签ID") @PathVariable Long id) { + Long userId = UserContext.getUserId(); tagService.delete(userId, id); return Result.success(); } diff --git a/src/main/java/cn/rosercode/finance/controller/TransactionController.java b/src/main/java/cn/rosercode/finance/controller/TransactionController.java index 1b355cc..bc1af65 100644 --- a/src/main/java/cn/rosercode/finance/controller/TransactionController.java +++ b/src/main/java/cn/rosercode/finance/controller/TransactionController.java @@ -1,24 +1,20 @@ package cn.rosercode.finance.controller; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; -import cn.rosercode.finance.common.PageResult; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.BatchCategoryRequest; -import cn.rosercode.finance.dto.BatchDeleteRequest; -import cn.rosercode.finance.dto.BatchTagRequest; -import cn.rosercode.finance.dto.TransactionQuery; -import cn.rosercode.finance.dto.TransactionRequest; -import cn.rosercode.finance.exporter.TransactionExportSheetHandler; -import cn.rosercode.finance.service.TransactionService; -import cn.rosercode.finance.vo.TransactionExportVO; -import cn.rosercode.finance.vo.TransactionVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import org.apache.poi.ss.usermodel.Sheet; @@ -32,13 +28,18 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Map; +import cn.rosercode.finance.common.PageResult; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.BatchCategoryRequest; +import cn.rosercode.finance.dto.BatchDeleteRequest; +import cn.rosercode.finance.dto.BatchTagRequest; +import cn.rosercode.finance.dto.TransactionQuery; +import cn.rosercode.finance.dto.TransactionRequest; +import cn.rosercode.finance.exporter.TransactionExportSheetHandler; +import cn.rosercode.finance.service.TransactionService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.TransactionExportVO; +import cn.rosercode.finance.vo.TransactionVO; /** * 交易记录控制器 @@ -70,29 +71,26 @@ public class TransactionController { /** * 分页查询交易记录 * - * @param request HTTP请求,从中获取用户ID * @param query 分页查询参数 * @return 分页交易记录列表 */ @GetMapping("/page") @Operation(summary = "分页查询交易记录", description = "根据查询条件分页获取当前用户的交易记录") - public Result> page(HttpServletRequest request, - @Parameter(description = "分页查询参数") TransactionQuery query) { - Long userId = (Long) request.getAttribute("userId"); + public Result> page(@Parameter(description = "分页查询参数") TransactionQuery query) { + Long userId = UserContext.getUserId(); return Result.success(transactionService.page(userId, query)); } /** * 新增交易记录 * - * @param request HTTP请求,从中获取用户ID * @param transactionRequest 交易记录请求参数 * @return 操作结果 */ @PostMapping @Operation(summary = "新增交易记录", description = "为当前用户新增一条交易记录") - public Result create(HttpServletRequest request, @RequestBody @Valid TransactionRequest transactionRequest) { - Long userId = (Long) request.getAttribute("userId"); + public Result create(@RequestBody @Valid TransactionRequest transactionRequest) { + Long userId = UserContext.getUserId(); transactionService.create(userId, transactionRequest); return Result.success(); } @@ -100,17 +98,15 @@ public class TransactionController { /** * 更新交易记录 * - * @param request HTTP请求,从中获取用户ID * @param id 交易记录ID * @param transactionRequest 交易记录请求参数 * @return 操作结果 */ @PutMapping("/{id}") @Operation(summary = "更新交易记录", description = "根据交易记录ID更新交易信息") - public Result update(HttpServletRequest request, - @Parameter(description = "交易记录ID") @PathVariable Long id, + public Result update(@Parameter(description = "交易记录ID") @PathVariable Long id, @RequestBody @Valid TransactionRequest transactionRequest) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); transactionService.update(userId, id, transactionRequest); return Result.success(); } @@ -118,15 +114,13 @@ public class TransactionController { /** * 删除交易记录 * - * @param request HTTP请求,从中获取用户ID * @param id 交易记录ID * @return 操作结果 */ @DeleteMapping("/{id}") @Operation(summary = "删除交易记录", description = "根据交易记录ID删除指定交易记录") - public Result delete(HttpServletRequest request, - @Parameter(description = "交易记录ID") @PathVariable Long id) { - Long userId = (Long) request.getAttribute("userId"); + public Result delete(@Parameter(description = "交易记录ID") @PathVariable Long id) { + Long userId = UserContext.getUserId(); transactionService.delete(userId, id); return Result.success(); } @@ -134,32 +128,28 @@ public class TransactionController { /** * 批量删除交易记录 * - * @param request HTTP请求,从中获取用户ID - * @param body 批量删除请求(记录ID列表) + * @param body 批量删除请求(记录ID列表) * @return 实际删除的记录数 */ @DeleteMapping("/batch") @Operation(summary = "批量删除交易记录", description = "根据记录ID列表批量删除当前用户的交易记录") - public Result batchDelete(HttpServletRequest request, - @RequestBody @Valid BatchDeleteRequest body) { - Long userId = (Long) request.getAttribute("userId"); + public Result batchDelete(@RequestBody @Valid BatchDeleteRequest body) { + Long userId = UserContext.getUserId(); return Result.success(transactionService.batchDelete(userId, body.getIds())); } /** * 切换交易记录的"计入统计"标记 * - * @param request HTTP请求,从中获取用户ID * @param id 交易记录ID * @param body 请求体,包含 includeInStats 字段 * @return 操作结果 */ @PutMapping("/{id}/stats") @Operation(summary = "切换计入统计标记", description = "切换交易记录是否计入统计(用于表格中开关)") - public Result toggleStats(HttpServletRequest request, - @Parameter(description = "交易记录ID") @PathVariable Long id, + public Result toggleStats(@Parameter(description = "交易记录ID") @PathVariable Long id, @RequestBody Map body) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); Boolean includeInStats = body != null ? body.get("includeInStats") : null; transactionService.toggleStats(userId, id, includeInStats); return Result.success(); @@ -168,17 +158,15 @@ public class TransactionController { /** * 切换交易记录的备注隐藏标记 * - * @param request HTTP请求,从中获取用户ID * @param id 交易记录ID * @param body 请求体,包含 remarkHidden 字段 * @return 操作结果 */ @PutMapping("/{id}/remark-hidden") @Operation(summary = "切换备注隐藏标记", description = "切换交易记录的备注是否隐藏(用于表格中单元格按钮)") - public Result toggleRemarkHidden(HttpServletRequest request, - @Parameter(description = "交易记录ID") @PathVariable Long id, + public Result toggleRemarkHidden(@Parameter(description = "交易记录ID") @PathVariable Long id, @RequestBody Map body) { - Long userId = (Long) request.getAttribute("userId"); + Long userId = UserContext.getUserId(); Boolean remarkHidden = body != null ? body.get("remarkHidden") : null; transactionService.toggleRemarkHidden(userId, id, remarkHidden); return Result.success(); @@ -187,47 +175,40 @@ public class TransactionController { /** * 批量设置交易记录的分类 * - * @param request HTTP请求,从中获取用户ID - * @param body 批量设置请求参数 + * @param body 批量设置请求参数 * @return 实际更新的记录数 */ @PutMapping("/batch-category") @Operation(summary = "批量设置分类", description = "将当前表格展示的多条交易记录按类型一键归入所选分类") - public Result batchUpdateCategory(HttpServletRequest request, - @RequestBody @Valid BatchCategoryRequest body) { - Long userId = (Long) request.getAttribute("userId"); + public Result batchUpdateCategory(@RequestBody @Valid BatchCategoryRequest body) { + Long userId = UserContext.getUserId(); return Result.success(transactionService.batchUpdateCategory(userId, body)); } /** * 批量给交易记录添加/移除标签 * - * @param request HTTP请求,从中获取用户ID - * @param body 批量标签请求参数 + * @param body 批量标签请求参数 * @return 涉及的记录数 */ @PutMapping("/batch-tags") @Operation(summary = "批量设置标签", description = "为选中的交易记录添加或移除标签") - public Result batchTags(HttpServletRequest request, - @RequestBody @Valid BatchTagRequest body) { - Long userId = (Long) request.getAttribute("userId"); + public Result batchTags(@RequestBody @Valid BatchTagRequest body) { + Long userId = UserContext.getUserId(); return Result.success(transactionService.batchTags(userId, body)); } /** * 导出交易记录为 Excel 文件 * - * @param request HTTP请求,从中获取用户ID - * @param response HTTP响应,用于输出文件流 * @param query 查询条件 * @throws IOException 文件输出异常 */ @GetMapping("/export") @Operation(summary = "导出交易记录", description = "根据查询条件导出交易记录为 Excel 文件") - public void export(HttpServletRequest request, - HttpServletResponse response, - @Parameter(description = "查询条件") TransactionQuery query) throws IOException { - Long userId = (Long) request.getAttribute("userId"); + public void export(@Parameter(description = "查询条件") TransactionQuery query) throws IOException { + Long userId = UserContext.getUserId(); + HttpServletResponse response = UserContext.getResponse(); // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); diff --git a/src/main/java/cn/rosercode/finance/controller/UserController.java b/src/main/java/cn/rosercode/finance/controller/UserController.java index 6281d72..274bd0f 100644 --- a/src/main/java/cn/rosercode/finance/controller/UserController.java +++ b/src/main/java/cn/rosercode/finance/controller/UserController.java @@ -1,17 +1,7 @@ package cn.rosercode.finance.controller; -import cn.rosercode.finance.common.Result; -import cn.rosercode.finance.dto.LoginRequest; -import cn.rosercode.finance.dto.RegisterRequest; -import cn.rosercode.finance.dto.RegistrationUpdateRequest; -import cn.rosercode.finance.service.RegistrationService; -import cn.rosercode.finance.service.UserService; -import cn.rosercode.finance.vo.LoginVO; -import cn.rosercode.finance.vo.RegistrationStatusVO; -import cn.rosercode.finance.vo.UserVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -20,6 +10,17 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import cn.rosercode.finance.common.Result; +import cn.rosercode.finance.dto.LoginRequest; +import cn.rosercode.finance.dto.RegisterRequest; +import cn.rosercode.finance.dto.RegistrationUpdateRequest; +import cn.rosercode.finance.service.RegistrationService; +import cn.rosercode.finance.service.UserService; +import cn.rosercode.finance.util.UserContext; +import cn.rosercode.finance.vo.LoginVO; +import cn.rosercode.finance.vo.RegistrationStatusVO; +import cn.rosercode.finance.vo.UserVO; + /** * 用户控制器 * @@ -90,13 +91,12 @@ public class UserController { /** * 获取当前用户信息 * - * @param request HTTP请求,从中获取用户ID * @return 用户信息 */ @GetMapping("/info") @Operation(summary = "获取当前用户信息", description = "获取已登录用户的详细信息") - public Result info(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); + public Result info() { + Long userId = UserContext.getUserId(); return Result.success(userService.getUserInfo(userId)); } } diff --git a/src/main/java/cn/rosercode/finance/converter/VoConverter.java b/src/main/java/cn/rosercode/finance/converter/VoConverter.java index 71b9b81..e2ca976 100644 --- a/src/main/java/cn/rosercode/finance/converter/VoConverter.java +++ b/src/main/java/cn/rosercode/finance/converter/VoConverter.java @@ -1,5 +1,11 @@ package cn.rosercode.finance.converter; +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + import cn.rosercode.finance.entity.AiUsage; import cn.rosercode.finance.entity.Category; import cn.rosercode.finance.entity.HttpLog; @@ -20,11 +26,6 @@ import cn.rosercode.finance.vo.RuleVO; import cn.rosercode.finance.vo.TagVO; import cn.rosercode.finance.vo.TransactionVO; import cn.rosercode.finance.vo.UserVO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.ReportingPolicy; - -import java.util.List; /** * 实体与视图对象之间的转换器,由 MapStruct 在编译期生成实现。 diff --git a/src/main/java/cn/rosercode/finance/dto/AiApplyRequest.java b/src/main/java/cn/rosercode/finance/dto/AiApplyRequest.java index 885edc4..bd637c2 100644 --- a/src/main/java/cn/rosercode/finance/dto/AiApplyRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/AiApplyRequest.java @@ -1,12 +1,13 @@ package cn.rosercode.finance.dto; -import cn.rosercode.finance.vo.AiNewTagVO; +import java.util.List; + import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; -import java.util.List; +import cn.rosercode.finance.vo.AiNewTagVO; /** * AI 建议应用请求DTO,携带逐条记录的建议分类与标签,用于一键写入。 diff --git a/src/main/java/cn/rosercode/finance/dto/AiSuggestOneRequest.java b/src/main/java/cn/rosercode/finance/dto/AiSuggestOneRequest.java index a776735..d5fa71e 100644 --- a/src/main/java/cn/rosercode/finance/dto/AiSuggestOneRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/AiSuggestOneRequest.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; +import java.math.BigDecimal; + import jakarta.validation.constraints.NotBlank; import lombok.Data; -import java.math.BigDecimal; - /** * 单条记录 AI 建议请求 DTO,携带表单当前内容用于生成分类与标签建议。 * diff --git a/src/main/java/cn/rosercode/finance/dto/AiSuggestRequest.java b/src/main/java/cn/rosercode/finance/dto/AiSuggestRequest.java index 2156064..334b3c3 100644 --- a/src/main/java/cn/rosercode/finance/dto/AiSuggestRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/AiSuggestRequest.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; +import java.util.List; + import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import java.util.List; - /** * AI 建议请求DTO,携带需要分析分类与标签的交易记录ID列表。 * diff --git a/src/main/java/cn/rosercode/finance/dto/BatchCategoryRequest.java b/src/main/java/cn/rosercode/finance/dto/BatchCategoryRequest.java index a1c99a0..4e6366e 100644 --- a/src/main/java/cn/rosercode/finance/dto/BatchCategoryRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/BatchCategoryRequest.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; +import java.util.List; + import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import java.util.List; - /** * 批量设置分类请求 DTO,用于将当前表格展示的多条交易记录一键归入分类。 * diff --git a/src/main/java/cn/rosercode/finance/dto/BatchDeleteRequest.java b/src/main/java/cn/rosercode/finance/dto/BatchDeleteRequest.java index 0c936a4..65f8a31 100644 --- a/src/main/java/cn/rosercode/finance/dto/BatchDeleteRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/BatchDeleteRequest.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; +import java.util.List; + import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import java.util.List; - /** * 批量删除交易记录请求DTO,用于一次性删除多条收支记录。 * diff --git a/src/main/java/cn/rosercode/finance/dto/BatchTagRequest.java b/src/main/java/cn/rosercode/finance/dto/BatchTagRequest.java index 40341d7..12c458c 100644 --- a/src/main/java/cn/rosercode/finance/dto/BatchTagRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/BatchTagRequest.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; +import java.util.List; + import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import java.util.List; - /** * 批量设置标签请求DTO,用于为多条交易记录添加或移除标签。 * diff --git a/src/main/java/cn/rosercode/finance/dto/BillImportRequest.java b/src/main/java/cn/rosercode/finance/dto/BillImportRequest.java index 1b6b401..643c8e3 100644 --- a/src/main/java/cn/rosercode/finance/dto/BillImportRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/BillImportRequest.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.dto; -import jakarta.validation.constraints.NotBlank; -import lombok.Data; - import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + /** * 账单导入请求 DTO,用于「后导入」阶段接收前端回传的勾选行。 * diff --git a/src/main/java/cn/rosercode/finance/dto/DatabaseConfigRequest.java b/src/main/java/cn/rosercode/finance/dto/DatabaseConfigRequest.java index a0e1608..fca0f8a 100644 --- a/src/main/java/cn/rosercode/finance/dto/DatabaseConfigRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/DatabaseConfigRequest.java @@ -1,8 +1,9 @@ package cn.rosercode.finance.dto; -import cn.rosercode.finance.config.datasource.DataSourceType; import lombok.Data; +import cn.rosercode.finance.config.datasource.DataSourceType; + /** * 数据库配置请求对象,用于提交 MySQL 或 SQLite 的连接信息。 * diff --git a/src/main/java/cn/rosercode/finance/dto/NotificationChannelRequest.java b/src/main/java/cn/rosercode/finance/dto/NotificationChannelRequest.java index 637ff35..b15eaa2 100644 --- a/src/main/java/cn/rosercode/finance/dto/NotificationChannelRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/NotificationChannelRequest.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.dto; -import lombok.Data; - import java.util.Map; +import lombok.Data; + /** * 通知渠道保存请求DTO,用于新增或修改消息推送渠道。 * diff --git a/src/main/java/cn/rosercode/finance/dto/RuleRequest.java b/src/main/java/cn/rosercode/finance/dto/RuleRequest.java index 12be6c1..186f19f 100644 --- a/src/main/java/cn/rosercode/finance/dto/RuleRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/RuleRequest.java @@ -1,11 +1,11 @@ package cn.rosercode.finance.dto; -import jakarta.validation.constraints.NotBlank; -import lombok.Data; - import java.math.BigDecimal; import java.util.List; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + /** * 规则请求DTO,用于接收创建或更新自定义分类与标签规则时提交的信息。 * diff --git a/src/main/java/cn/rosercode/finance/dto/StatisticsRangeQuery.java b/src/main/java/cn/rosercode/finance/dto/StatisticsRangeQuery.java index a235c3d..fd20cce 100644 --- a/src/main/java/cn/rosercode/finance/dto/StatisticsRangeQuery.java +++ b/src/main/java/cn/rosercode/finance/dto/StatisticsRangeQuery.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; -import lombok.Data; - import java.math.BigDecimal; import java.util.List; +import lombok.Data; + /** * 统计查询 DTO,用于按时间区间及可选过滤条件进行统计。 * diff --git a/src/main/java/cn/rosercode/finance/dto/TransactionQuery.java b/src/main/java/cn/rosercode/finance/dto/TransactionQuery.java index 12ea4ae..50a9b4f 100644 --- a/src/main/java/cn/rosercode/finance/dto/TransactionQuery.java +++ b/src/main/java/cn/rosercode/finance/dto/TransactionQuery.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.dto; -import lombok.Data; - import java.math.BigDecimal; import java.util.List; +import lombok.Data; + /** * 交易查询DTO,用于接收交易记录分页查询的筛选条件 */ diff --git a/src/main/java/cn/rosercode/finance/dto/TransactionRequest.java b/src/main/java/cn/rosercode/finance/dto/TransactionRequest.java index 7ab3b0c..df7e359 100644 --- a/src/main/java/cn/rosercode/finance/dto/TransactionRequest.java +++ b/src/main/java/cn/rosercode/finance/dto/TransactionRequest.java @@ -1,14 +1,14 @@ package cn.rosercode.finance.dto; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.List; - /** * 交易请求DTO,用于接收创建或更新交易记录时提交的信息 */ diff --git a/src/main/java/cn/rosercode/finance/entity/AiConfig.java b/src/main/java/cn/rosercode/finance/entity/AiConfig.java index 4c26fe9..f11a585 100644 --- a/src/main/java/cn/rosercode/finance/entity/AiConfig.java +++ b/src/main/java/cn/rosercode/finance/entity/AiConfig.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * AI 配置实体类,对应数据库表 t_ai_config,用于记录用户的 AI 智能建议配置。 * diff --git a/src/main/java/cn/rosercode/finance/entity/AiUsage.java b/src/main/java/cn/rosercode/finance/entity/AiUsage.java index 6f1aa97..9ab0b27 100644 --- a/src/main/java/cn/rosercode/finance/entity/AiUsage.java +++ b/src/main/java/cn/rosercode/finance/entity/AiUsage.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * AI 使用记录实体类,对应数据库表 t_ai_usage,记录每次调用 DeepSeek 的用量与结果。 * diff --git a/src/main/java/cn/rosercode/finance/entity/Category.java b/src/main/java/cn/rosercode/finance/entity/Category.java index ad1a259..2747dd5 100644 --- a/src/main/java/cn/rosercode/finance/entity/Category.java +++ b/src/main/java/cn/rosercode/finance/entity/Category.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 分类实体类,对应数据库表 t_category,用于记录收支分类信息 */ diff --git a/src/main/java/cn/rosercode/finance/entity/HttpLog.java b/src/main/java/cn/rosercode/finance/entity/HttpLog.java index c12ca49..10fe1ed 100644 --- a/src/main/java/cn/rosercode/finance/entity/HttpLog.java +++ b/src/main/java/cn/rosercode/finance/entity/HttpLog.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * HTTP 访问日志实体类,对应数据库表 t_http_log,记录每次接口请求的方法、路径、状态与耗时。 * diff --git a/src/main/java/cn/rosercode/finance/entity/LoginLog.java b/src/main/java/cn/rosercode/finance/entity/LoginLog.java index 1e86179..0ad5981 100644 --- a/src/main/java/cn/rosercode/finance/entity/LoginLog.java +++ b/src/main/java/cn/rosercode/finance/entity/LoginLog.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 登录日志实体类,对应数据库表 t_login_log,记录每次登录尝试的结果。 * diff --git a/src/main/java/cn/rosercode/finance/entity/NotificationChannel.java b/src/main/java/cn/rosercode/finance/entity/NotificationChannel.java index 1bf6a97..312d2c6 100644 --- a/src/main/java/cn/rosercode/finance/entity/NotificationChannel.java +++ b/src/main/java/cn/rosercode/finance/entity/NotificationChannel.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 通知渠道实体类,对应数据库表 t_notification_channel,用于记录用户配置的消息推送渠道。 * diff --git a/src/main/java/cn/rosercode/finance/entity/NotificationConfig.java b/src/main/java/cn/rosercode/finance/entity/NotificationConfig.java index 7ff7d84..1913512 100644 --- a/src/main/java/cn/rosercode/finance/entity/NotificationConfig.java +++ b/src/main/java/cn/rosercode/finance/entity/NotificationConfig.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 通知配置实体类,对应数据库表 t_notification_config,用于记录用户的账单导入提醒周期。 * diff --git a/src/main/java/cn/rosercode/finance/entity/NotificationLog.java b/src/main/java/cn/rosercode/finance/entity/NotificationLog.java index c4bdaed..2169282 100644 --- a/src/main/java/cn/rosercode/finance/entity/NotificationLog.java +++ b/src/main/java/cn/rosercode/finance/entity/NotificationLog.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 通知日志实体类,对应数据库表 t_notification_log,用于记录每次提醒的发送结果。 * diff --git a/src/main/java/cn/rosercode/finance/entity/Rule.java b/src/main/java/cn/rosercode/finance/entity/Rule.java index 9887764..9dafddb 100644 --- a/src/main/java/cn/rosercode/finance/entity/Rule.java +++ b/src/main/java/cn/rosercode/finance/entity/Rule.java @@ -1,13 +1,13 @@ package cn.rosercode.finance.entity; +import java.math.BigDecimal; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - /** * 自定义分类与标签规则实体类,对应数据库表 t_rule。 * diff --git a/src/main/java/cn/rosercode/finance/entity/RuleTag.java b/src/main/java/cn/rosercode/finance/entity/RuleTag.java index ea61d3e..5caf948 100644 --- a/src/main/java/cn/rosercode/finance/entity/RuleTag.java +++ b/src/main/java/cn/rosercode/finance/entity/RuleTag.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 规则标签关联实体类,对应数据库表 t_rule_tag,表示规则与标签的多对多关系。 * diff --git a/src/main/java/cn/rosercode/finance/entity/Tag.java b/src/main/java/cn/rosercode/finance/entity/Tag.java index 95414e1..d0378fd 100644 --- a/src/main/java/cn/rosercode/finance/entity/Tag.java +++ b/src/main/java/cn/rosercode/finance/entity/Tag.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 标签实体类,对应数据库表 t_tag,用于记录用户自定义标签。 * diff --git a/src/main/java/cn/rosercode/finance/entity/Transaction.java b/src/main/java/cn/rosercode/finance/entity/Transaction.java index 8b2a2cf..75b5453 100644 --- a/src/main/java/cn/rosercode/finance/entity/Transaction.java +++ b/src/main/java/cn/rosercode/finance/entity/Transaction.java @@ -1,13 +1,13 @@ package cn.rosercode.finance.entity; +import java.math.BigDecimal; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - /** * 交易记录实体类,对应数据库表 t_transaction,用于记录每笔收支交易 */ diff --git a/src/main/java/cn/rosercode/finance/entity/TransactionTag.java b/src/main/java/cn/rosercode/finance/entity/TransactionTag.java index de63ae0..6846541 100644 --- a/src/main/java/cn/rosercode/finance/entity/TransactionTag.java +++ b/src/main/java/cn/rosercode/finance/entity/TransactionTag.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 交易标签关联实体类,对应数据库表 t_transaction_tag,维护交易与标签的多对多关系。 * diff --git a/src/main/java/cn/rosercode/finance/entity/User.java b/src/main/java/cn/rosercode/finance/entity/User.java index 4f82f6f..05a12de 100644 --- a/src/main/java/cn/rosercode/finance/entity/User.java +++ b/src/main/java/cn/rosercode/finance/entity/User.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.entity; +import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import java.time.LocalDateTime; - /** * 用户实体类,对应数据库表 t_user */ diff --git a/src/main/java/cn/rosercode/finance/importer/AlipayBillParser.java b/src/main/java/cn/rosercode/finance/importer/AlipayBillParser.java index 62b3dba..8901b10 100644 --- a/src/main/java/cn/rosercode/finance/importer/AlipayBillParser.java +++ b/src/main/java/cn/rosercode/finance/importer/AlipayBillParser.java @@ -1,14 +1,5 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.enums.BillType; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -24,6 +15,16 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import cn.rosercode.finance.enums.BillType; + /** * 支付宝账单解析器。 * diff --git a/src/main/java/cn/rosercode/finance/importer/BillArchiveExtractor.java b/src/main/java/cn/rosercode/finance/importer/BillArchiveExtractor.java index 4f1477d..88a0bf2 100644 --- a/src/main/java/cn/rosercode/finance/importer/BillArchiveExtractor.java +++ b/src/main/java/cn/rosercode/finance/importer/BillArchiveExtractor.java @@ -1,11 +1,5 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.common.BusinessException; -import net.lingala.zip4j.ZipFile; -import net.lingala.zip4j.exception.ZipException; -import net.lingala.zip4j.model.FileHeader; -import org.springframework.stereotype.Component; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -13,6 +7,13 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.FileHeader; +import org.springframework.stereotype.Component; + +import cn.rosercode.finance.common.BusinessException; + /** * 账单压缩包解压工具。 * diff --git a/src/main/java/cn/rosercode/finance/importer/BillParser.java b/src/main/java/cn/rosercode/finance/importer/BillParser.java index 18d43e6..1f680f8 100644 --- a/src/main/java/cn/rosercode/finance/importer/BillParser.java +++ b/src/main/java/cn/rosercode/finance/importer/BillParser.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.enums.BillType; - import java.io.IOException; import java.util.List; +import cn.rosercode.finance.enums.BillType; + /** * 账单解析器接口,定义不同账单来源的解析行为。 * diff --git a/src/main/java/cn/rosercode/finance/importer/BocomBillParser.java b/src/main/java/cn/rosercode/finance/importer/BocomBillParser.java index adac8b4..9889c57 100644 --- a/src/main/java/cn/rosercode/finance/importer/BocomBillParser.java +++ b/src/main/java/cn/rosercode/finance/importer/BocomBillParser.java @@ -1,17 +1,5 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.enums.BillType; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellType; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.WorkbookFactory; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigDecimal; @@ -25,6 +13,19 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import cn.rosercode.finance.enums.BillType; + /** * 交通银行账单解析器。 * diff --git a/src/main/java/cn/rosercode/finance/importer/CmbBillParser.java b/src/main/java/cn/rosercode/finance/importer/CmbBillParser.java index fcbd892..c2de9e4 100644 --- a/src/main/java/cn/rosercode/finance/importer/CmbBillParser.java +++ b/src/main/java/cn/rosercode/finance/importer/CmbBillParser.java @@ -1,14 +1,5 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.enums.BillType; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; @@ -27,6 +18,16 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import cn.rosercode.finance.enums.BillType; + /** * 招商银行账单解析器。 * diff --git a/src/main/java/cn/rosercode/finance/importer/CmbPdfParser.java b/src/main/java/cn/rosercode/finance/importer/CmbPdfParser.java index 8b0562e..ff9f2ad 100644 --- a/src/main/java/cn/rosercode/finance/importer/CmbPdfParser.java +++ b/src/main/java/cn/rosercode/finance/importer/CmbPdfParser.java @@ -1,14 +1,5 @@ package cn.rosercode.finance.importer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.pdfbox.Loader; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.text.PDFTextStripper; -import org.apache.pdfbox.text.TextPosition; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; @@ -22,6 +13,15 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.text.PDFTextStripper; +import org.apache.pdfbox.text.TextPosition; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + /** * 招商银行「交易流水」PDF 解析器。 * diff --git a/src/main/java/cn/rosercode/finance/importer/MeituanBillParser.java b/src/main/java/cn/rosercode/finance/importer/MeituanBillParser.java index 3842d29..be5081e 100644 --- a/src/main/java/cn/rosercode/finance/importer/MeituanBillParser.java +++ b/src/main/java/cn/rosercode/finance/importer/MeituanBillParser.java @@ -1,14 +1,5 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.enums.BillType; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -24,6 +15,16 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import cn.rosercode.finance.enums.BillType; + /** * 美团账单解析器。 * diff --git a/src/main/java/cn/rosercode/finance/importer/ParsedBillRow.java b/src/main/java/cn/rosercode/finance/importer/ParsedBillRow.java index c2276e3..b37ec85 100644 --- a/src/main/java/cn/rosercode/finance/importer/ParsedBillRow.java +++ b/src/main/java/cn/rosercode/finance/importer/ParsedBillRow.java @@ -1,12 +1,13 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.vo.TagVO; -import lombok.Data; - import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; +import lombok.Data; + +import cn.rosercode.finance.vo.TagVO; + /** * 账单解析结果行,作为「先解析、后导入」流程中解析阶段的中间对象。 * diff --git a/src/main/java/cn/rosercode/finance/importer/WechatBillParser.java b/src/main/java/cn/rosercode/finance/importer/WechatBillParser.java index eb315f6..76cf4f8 100644 --- a/src/main/java/cn/rosercode/finance/importer/WechatBillParser.java +++ b/src/main/java/cn/rosercode/finance/importer/WechatBillParser.java @@ -1,17 +1,5 @@ package cn.rosercode.finance.importer; -import cn.rosercode.finance.enums.BillType; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellType; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.WorkbookFactory; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigDecimal; @@ -23,6 +11,19 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import cn.rosercode.finance.enums.BillType; + /** * 微信支付账单解析器。 * diff --git a/src/main/java/cn/rosercode/finance/interceptor/AccessLogInterceptor.java b/src/main/java/cn/rosercode/finance/interceptor/AccessLogInterceptor.java index 7c2fc27..a7f2e9c 100644 --- a/src/main/java/cn/rosercode/finance/interceptor/AccessLogInterceptor.java +++ b/src/main/java/cn/rosercode/finance/interceptor/AccessLogInterceptor.java @@ -1,14 +1,14 @@ package cn.rosercode.finance.interceptor; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + import cn.rosercode.finance.entity.HttpLog; import cn.rosercode.finance.service.LogService; import cn.rosercode.finance.util.RequestUtils; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; /** * HTTP 访问日志拦截器。 @@ -19,10 +19,9 @@ import org.springframework.web.servlet.HandlerInterceptor; * @date 2026-09-18 16:53:21 */ @Component +@Slf4j public class AccessLogInterceptor implements HandlerInterceptor { - private static final Logger log = LoggerFactory.getLogger(AccessLogInterceptor.class); - /** 请求属性名:请求开始时间 */ private static final String START_TIME_ATTR = "accessLogStartTime"; diff --git a/src/main/java/cn/rosercode/finance/interceptor/JwtInterceptor.java b/src/main/java/cn/rosercode/finance/interceptor/JwtInterceptor.java index 703e032..f7d4c9d 100644 --- a/src/main/java/cn/rosercode/finance/interceptor/JwtInterceptor.java +++ b/src/main/java/cn/rosercode/finance/interceptor/JwtInterceptor.java @@ -1,15 +1,16 @@ package cn.rosercode.finance.interceptor; -import cn.rosercode.finance.config.datasource.DataSourceContextHolder; -import cn.rosercode.finance.util.JwtUtil; +import java.util.HashMap; +import java.util.Map; + import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; -import java.util.HashMap; -import java.util.Map; +import cn.rosercode.finance.config.datasource.DataSourceContextHolder; +import cn.rosercode.finance.util.JwtUtil; /** * JWT 认证拦截器。 diff --git a/src/main/java/cn/rosercode/finance/mapper/AiConfigMapper.java b/src/main/java/cn/rosercode/finance/mapper/AiConfigMapper.java index 72b1a26..1e9a37b 100644 --- a/src/main/java/cn/rosercode/finance/mapper/AiConfigMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/AiConfigMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.AiConfig; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.AiConfig; + /** * AI 配置Mapper接口,继承MyBatis-Plus的BaseMapper,提供AI配置表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/AiUsageMapper.java b/src/main/java/cn/rosercode/finance/mapper/AiUsageMapper.java index 285e9f0..2f1e44c 100644 --- a/src/main/java/cn/rosercode/finance/mapper/AiUsageMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/AiUsageMapper.java @@ -1,12 +1,13 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.AiUsage; -import cn.rosercode.finance.vo.AiUsageSummaryVO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import cn.rosercode.finance.entity.AiUsage; +import cn.rosercode.finance.vo.AiUsageSummaryVO; + /** * AI 使用记录Mapper接口,继承MyBatis-Plus的BaseMapper,并提供汇总统计查询。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/CategoryMapper.java b/src/main/java/cn/rosercode/finance/mapper/CategoryMapper.java index 5cc9f9a..10b9cb3 100644 --- a/src/main/java/cn/rosercode/finance/mapper/CategoryMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/CategoryMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.Category; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.Category; + /** * 分类Mapper接口,继承MyBatis-Plus的BaseMapper,提供分类表的基础CRUD操作 */ diff --git a/src/main/java/cn/rosercode/finance/mapper/HttpLogMapper.java b/src/main/java/cn/rosercode/finance/mapper/HttpLogMapper.java index 128ca1d..16cb8a3 100644 --- a/src/main/java/cn/rosercode/finance/mapper/HttpLogMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/HttpLogMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.HttpLog; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.HttpLog; + /** * HTTP 访问日志Mapper接口,继承MyBatis-Plus的BaseMapper,提供访问日志表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/LoginLogMapper.java b/src/main/java/cn/rosercode/finance/mapper/LoginLogMapper.java index 71238ac..00e8724 100644 --- a/src/main/java/cn/rosercode/finance/mapper/LoginLogMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/LoginLogMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.LoginLog; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.LoginLog; + /** * 登录日志Mapper接口,继承MyBatis-Plus的BaseMapper,提供登录日志表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/NotificationChannelMapper.java b/src/main/java/cn/rosercode/finance/mapper/NotificationChannelMapper.java index 007907f..a77d081 100644 --- a/src/main/java/cn/rosercode/finance/mapper/NotificationChannelMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/NotificationChannelMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.NotificationChannel; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.NotificationChannel; + /** * 通知渠道Mapper接口,继承MyBatis-Plus的BaseMapper,提供通知渠道表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/NotificationConfigMapper.java b/src/main/java/cn/rosercode/finance/mapper/NotificationConfigMapper.java index 5c1c3df..294c91a 100644 --- a/src/main/java/cn/rosercode/finance/mapper/NotificationConfigMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/NotificationConfigMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.NotificationConfig; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.NotificationConfig; + /** * 通知配置Mapper接口,继承MyBatis-Plus的BaseMapper,提供通知配置表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/NotificationLogMapper.java b/src/main/java/cn/rosercode/finance/mapper/NotificationLogMapper.java index 724637d..7403e76 100644 --- a/src/main/java/cn/rosercode/finance/mapper/NotificationLogMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/NotificationLogMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.NotificationLog; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.NotificationLog; + /** * 通知日志Mapper接口,继承MyBatis-Plus的BaseMapper,提供通知日志表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/RuleMapper.java b/src/main/java/cn/rosercode/finance/mapper/RuleMapper.java index c77df60..e138d16 100644 --- a/src/main/java/cn/rosercode/finance/mapper/RuleMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/RuleMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.Rule; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.Rule; + /** * 规则Mapper接口,继承MyBatis-Plus的BaseMapper,提供规则表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/RuleTagMapper.java b/src/main/java/cn/rosercode/finance/mapper/RuleTagMapper.java index 740c102..5679f04 100644 --- a/src/main/java/cn/rosercode/finance/mapper/RuleTagMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/RuleTagMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.RuleTag; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.RuleTag; + /** * 规则标签关联Mapper接口,继承MyBatis-Plus的BaseMapper,提供规则标签关联表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/TagMapper.java b/src/main/java/cn/rosercode/finance/mapper/TagMapper.java index d9f8842..d90af33 100644 --- a/src/main/java/cn/rosercode/finance/mapper/TagMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/TagMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.Tag; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.Tag; + /** * 标签Mapper接口,继承MyBatis-Plus的BaseMapper,提供标签表的基础CRUD操作。 * diff --git a/src/main/java/cn/rosercode/finance/mapper/TransactionMapper.java b/src/main/java/cn/rosercode/finance/mapper/TransactionMapper.java index ed38e67..e90c40d 100644 --- a/src/main/java/cn/rosercode/finance/mapper/TransactionMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/TransactionMapper.java @@ -1,11 +1,12 @@ package cn.rosercode.finance.mapper; +import java.util.List; + import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.Transaction; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.util.List; +import cn.rosercode.finance.entity.Transaction; /** * 交易记录Mapper接口,继承MyBatis-Plus的BaseMapper,提供交易记录表的基础CRUD操作 diff --git a/src/main/java/cn/rosercode/finance/mapper/TransactionTagMapper.java b/src/main/java/cn/rosercode/finance/mapper/TransactionTagMapper.java index 3e29f91..2752b75 100644 --- a/src/main/java/cn/rosercode/finance/mapper/TransactionTagMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/TransactionTagMapper.java @@ -1,11 +1,12 @@ package cn.rosercode.finance.mapper; +import java.util.List; + import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.TransactionTag; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.util.List; +import cn.rosercode.finance.entity.TransactionTag; /** * 交易标签关联Mapper接口,继承MyBatis-Plus的BaseMapper,提供关联表的基础CRUD操作。 diff --git a/src/main/java/cn/rosercode/finance/mapper/UserMapper.java b/src/main/java/cn/rosercode/finance/mapper/UserMapper.java index 1ec6809..79d289f 100644 --- a/src/main/java/cn/rosercode/finance/mapper/UserMapper.java +++ b/src/main/java/cn/rosercode/finance/mapper/UserMapper.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import cn.rosercode.finance.entity.User; import org.apache.ibatis.annotations.Mapper; +import cn.rosercode.finance.entity.User; + /** * 用户Mapper接口,继承MyBatis-Plus的BaseMapper,提供用户表的基础CRUD操作 */ diff --git a/src/main/java/cn/rosercode/finance/notification/AbstractHttpNotificationSender.java b/src/main/java/cn/rosercode/finance/notification/AbstractHttpNotificationSender.java index e9e3aa3..1b6dc8c 100644 --- a/src/main/java/cn/rosercode/finance/notification/AbstractHttpNotificationSender.java +++ b/src/main/java/cn/rosercode/finance/notification/AbstractHttpNotificationSender.java @@ -1,9 +1,5 @@ package cn.rosercode.finance.notification; -import cn.rosercode.finance.common.BusinessException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import java.io.IOException; import java.net.URI; import java.net.URLEncoder; @@ -14,6 +10,11 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import cn.rosercode.finance.common.BusinessException; + /** * HTTP 通知发送器抽象基类。 * diff --git a/src/main/java/cn/rosercode/finance/notification/EmailNotificationSender.java b/src/main/java/cn/rosercode/finance/notification/EmailNotificationSender.java index 97b5780..863209e 100644 --- a/src/main/java/cn/rosercode/finance/notification/EmailNotificationSender.java +++ b/src/main/java/cn/rosercode/finance/notification/EmailNotificationSender.java @@ -1,7 +1,8 @@ package cn.rosercode.finance.notification; -import cn.rosercode.finance.common.BusinessException; -import cn.rosercode.finance.enums.NotificationChannelType; +import java.util.Map; +import java.util.Properties; + import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import org.springframework.mail.MailException; @@ -9,8 +10,8 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component; -import java.util.Map; -import java.util.Properties; +import cn.rosercode.finance.common.BusinessException; +import cn.rosercode.finance.enums.NotificationChannelType; /** * 邮箱通知发送器,通过 SMTP 发送纯文本邮件(兼容 QQ 邮箱)。 diff --git a/src/main/java/cn/rosercode/finance/notification/NotificationSender.java b/src/main/java/cn/rosercode/finance/notification/NotificationSender.java index 30399c4..6fe58f8 100644 --- a/src/main/java/cn/rosercode/finance/notification/NotificationSender.java +++ b/src/main/java/cn/rosercode/finance/notification/NotificationSender.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.notification; -import cn.rosercode.finance.enums.NotificationChannelType; - import java.util.Map; +import cn.rosercode.finance.enums.NotificationChannelType; + /** * 通知发送器接口,定义不同渠道的消息推送能力。 * diff --git a/src/main/java/cn/rosercode/finance/notification/NotificationSenderFactory.java b/src/main/java/cn/rosercode/finance/notification/NotificationSenderFactory.java index eda15b4..b678b29 100644 --- a/src/main/java/cn/rosercode/finance/notification/NotificationSenderFactory.java +++ b/src/main/java/cn/rosercode/finance/notification/NotificationSenderFactory.java @@ -1,13 +1,14 @@ package cn.rosercode.finance.notification; -import cn.rosercode.finance.common.BusinessException; -import cn.rosercode.finance.enums.NotificationChannelType; -import org.springframework.stereotype.Component; - import java.util.EnumMap; import java.util.List; import java.util.Map; +import org.springframework.stereotype.Component; + +import cn.rosercode.finance.common.BusinessException; +import cn.rosercode.finance.enums.NotificationChannelType; + /** * 通知发送器工厂,按渠道类型分发到对应的发送器实现。 * diff --git a/src/main/java/cn/rosercode/finance/notification/ServerChanNotificationSender.java b/src/main/java/cn/rosercode/finance/notification/ServerChanNotificationSender.java index f7d7e8f..af10cbf 100644 --- a/src/main/java/cn/rosercode/finance/notification/ServerChanNotificationSender.java +++ b/src/main/java/cn/rosercode/finance/notification/ServerChanNotificationSender.java @@ -1,14 +1,15 @@ package cn.rosercode.finance.notification; -import cn.rosercode.finance.common.BusinessException; -import cn.rosercode.finance.enums.NotificationChannelType; +import java.util.LinkedHashMap; +import java.util.Map; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; -import java.util.LinkedHashMap; -import java.util.Map; +import cn.rosercode.finance.common.BusinessException; +import cn.rosercode.finance.enums.NotificationChannelType; /** * Server 酱通知发送器,通过 Server 酱 Turbo 接口将消息推送到个人微信。 diff --git a/src/main/java/cn/rosercode/finance/scheduler/LogCleanupScheduler.java b/src/main/java/cn/rosercode/finance/scheduler/LogCleanupScheduler.java index 8f81111..e74cdf6 100644 --- a/src/main/java/cn/rosercode/finance/scheduler/LogCleanupScheduler.java +++ b/src/main/java/cn/rosercode/finance/scheduler/LogCleanupScheduler.java @@ -1,18 +1,18 @@ package cn.rosercode.finance.scheduler; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + import cn.rosercode.finance.config.app.AppConfig; import cn.rosercode.finance.config.app.AppConfigStore; import cn.rosercode.finance.entity.HttpLog; import cn.rosercode.finance.entity.LoginLog; import cn.rosercode.finance.mapper.HttpLogMapper; import cn.rosercode.finance.mapper.LoginLogMapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; /** * 日志清理调度器。 @@ -23,10 +23,9 @@ import java.time.LocalDateTime; * @date 2026-09-18 17:15:49 */ @Component +@Slf4j public class LogCleanupScheduler { - private static final Logger log = LoggerFactory.getLogger(LogCleanupScheduler.class); - private final AppConfigStore appConfigStore; private final HttpLogMapper httpLogMapper; private final LoginLogMapper loginLogMapper; diff --git a/src/main/java/cn/rosercode/finance/scheduler/NotificationScheduler.java b/src/main/java/cn/rosercode/finance/scheduler/NotificationScheduler.java index 78d8e7a..65d4e87 100644 --- a/src/main/java/cn/rosercode/finance/scheduler/NotificationScheduler.java +++ b/src/main/java/cn/rosercode/finance/scheduler/NotificationScheduler.java @@ -1,17 +1,5 @@ package cn.rosercode.finance.scheduler; -import cn.rosercode.finance.entity.NotificationChannel; -import cn.rosercode.finance.entity.NotificationConfig; -import cn.rosercode.finance.enums.NotificationFrequency; -import cn.rosercode.finance.mapper.NotificationConfigMapper; -import cn.rosercode.finance.service.NotificationChannelService; -import cn.rosercode.finance.service.NotificationService; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -19,6 +7,18 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.List; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import cn.rosercode.finance.entity.NotificationChannel; +import cn.rosercode.finance.entity.NotificationConfig; +import cn.rosercode.finance.enums.NotificationFrequency; +import cn.rosercode.finance.mapper.NotificationConfigMapper; +import cn.rosercode.finance.service.NotificationChannelService; +import cn.rosercode.finance.service.NotificationService; + /** * 通知调度器。 * @@ -28,10 +28,9 @@ import java.util.List; * @date 2026-09-18 12:38:30 */ @Component +@Slf4j public class NotificationScheduler { - private static final Logger log = LoggerFactory.getLogger(NotificationScheduler.class); - /** 时间格式 */ private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); diff --git a/src/main/java/cn/rosercode/finance/service/AiSuggestionService.java b/src/main/java/cn/rosercode/finance/service/AiSuggestionService.java index a37bc3a..4c756a1 100644 --- a/src/main/java/cn/rosercode/finance/service/AiSuggestionService.java +++ b/src/main/java/cn/rosercode/finance/service/AiSuggestionService.java @@ -1,11 +1,11 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.dto.AiApplyRequest; import cn.rosercode.finance.dto.AiSuggestOneRequest; import cn.rosercode.finance.vo.AiSuggestionVO; -import java.util.List; - /** * AI 建议服务接口,提供交易记录的分类与标签智能建议及应用能力。 * diff --git a/src/main/java/cn/rosercode/finance/service/AppLogStreamService.java b/src/main/java/cn/rosercode/finance/service/AppLogStreamService.java index f32e185..f9df2b8 100644 --- a/src/main/java/cn/rosercode/finance/service/AppLogStreamService.java +++ b/src/main/java/cn/rosercode/finance/service/AppLogStreamService.java @@ -1,10 +1,5 @@ package cn.rosercode.finance.service; -import cn.rosercode.finance.util.AppLogUtils; -import jakarta.annotation.PreDestroy; -import org.springframework.stereotype.Service; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.RandomAccessFile; @@ -18,6 +13,12 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import jakarta.annotation.PreDestroy; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import cn.rosercode.finance.util.AppLogUtils; + /** * 应用运行日志实时推送服务。 * diff --git a/src/main/java/cn/rosercode/finance/service/BillImportService.java b/src/main/java/cn/rosercode/finance/service/BillImportService.java index 820753e..736a457 100644 --- a/src/main/java/cn/rosercode/finance/service/BillImportService.java +++ b/src/main/java/cn/rosercode/finance/service/BillImportService.java @@ -1,12 +1,12 @@ package cn.rosercode.finance.service; +import java.io.IOException; + import cn.rosercode.finance.dto.BillImportRequest; import cn.rosercode.finance.enums.BillType; import cn.rosercode.finance.vo.BillImportResultVO; import cn.rosercode.finance.vo.BillParseResultVO; -import java.io.IOException; - /** * 账单导入服务,负责「先解析、后导入」两阶段的业务逻辑。 * diff --git a/src/main/java/cn/rosercode/finance/service/CategoryService.java b/src/main/java/cn/rosercode/finance/service/CategoryService.java index e55a482..db96c0f 100644 --- a/src/main/java/cn/rosercode/finance/service/CategoryService.java +++ b/src/main/java/cn/rosercode/finance/service/CategoryService.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.dto.CategoryRequest; import cn.rosercode.finance.vo.CategoryVO; -import java.util.List; - /** * 分类服务接口,提供收支分类的增删改查操作。 */ diff --git a/src/main/java/cn/rosercode/finance/service/NotificationChannelService.java b/src/main/java/cn/rosercode/finance/service/NotificationChannelService.java index dc0f92a..7ff8f7e 100644 --- a/src/main/java/cn/rosercode/finance/service/NotificationChannelService.java +++ b/src/main/java/cn/rosercode/finance/service/NotificationChannelService.java @@ -1,11 +1,11 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.dto.NotificationChannelRequest; import cn.rosercode.finance.entity.NotificationChannel; import cn.rosercode.finance.vo.NotificationChannelVO; -import java.util.List; - /** * 通知渠道服务接口,负责用户消息推送渠道的增删改查。 * diff --git a/src/main/java/cn/rosercode/finance/service/NotificationService.java b/src/main/java/cn/rosercode/finance/service/NotificationService.java index 29de273..5db5ace 100644 --- a/src/main/java/cn/rosercode/finance/service/NotificationService.java +++ b/src/main/java/cn/rosercode/finance/service/NotificationService.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.entity.NotificationChannel; import cn.rosercode.finance.vo.NotificationLogVO; -import java.util.List; - /** * 通知发送服务接口,负责消息的实际推送与发送日志记录。 * diff --git a/src/main/java/cn/rosercode/finance/service/RuleMatchContext.java b/src/main/java/cn/rosercode/finance/service/RuleMatchContext.java index 385a441..3b81768 100644 --- a/src/main/java/cn/rosercode/finance/service/RuleMatchContext.java +++ b/src/main/java/cn/rosercode/finance/service/RuleMatchContext.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.service; +import java.math.BigDecimal; + import lombok.AllArgsConstructor; import lombok.Data; -import java.math.BigDecimal; - /** * 规则匹配上下文,承载单条交易的匹配所需信息。 * diff --git a/src/main/java/cn/rosercode/finance/service/RuleMatchResult.java b/src/main/java/cn/rosercode/finance/service/RuleMatchResult.java index cf09ab5..7318fac 100644 --- a/src/main/java/cn/rosercode/finance/service/RuleMatchResult.java +++ b/src/main/java/cn/rosercode/finance/service/RuleMatchResult.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.service; -import lombok.Data; - import java.util.ArrayList; import java.util.List; +import lombok.Data; + /** * 规则匹配结果,承载命中规则后建议应用的分类与标签。 * diff --git a/src/main/java/cn/rosercode/finance/service/RuleService.java b/src/main/java/cn/rosercode/finance/service/RuleService.java index 6304e6a..588bccb 100644 --- a/src/main/java/cn/rosercode/finance/service/RuleService.java +++ b/src/main/java/cn/rosercode/finance/service/RuleService.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.dto.RuleRequest; import cn.rosercode.finance.vo.RuleVO; -import java.util.List; - /** * 规则服务,负责自定义分类与标签规则的增删改查及匹配。 * diff --git a/src/main/java/cn/rosercode/finance/service/StatisticsService.java b/src/main/java/cn/rosercode/finance/service/StatisticsService.java index e2ac0f7..70d1975 100644 --- a/src/main/java/cn/rosercode/finance/service/StatisticsService.java +++ b/src/main/java/cn/rosercode/finance/service/StatisticsService.java @@ -1,5 +1,7 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.dto.StatisticsQuery; import cn.rosercode.finance.dto.StatisticsRangeQuery; import cn.rosercode.finance.enums.StatisticsDimension; @@ -9,8 +11,6 @@ import cn.rosercode.finance.vo.StatisticsGroupVO; import cn.rosercode.finance.vo.StatisticsSummaryVO; import cn.rosercode.finance.vo.TrendStatisticsVO; -import java.util.List; - /** * 统计服务接口,提供月度收支统计、分类统计和趋势分析功能。 */ diff --git a/src/main/java/cn/rosercode/finance/service/TagService.java b/src/main/java/cn/rosercode/finance/service/TagService.java index dcf4d06..d4b7dcc 100644 --- a/src/main/java/cn/rosercode/finance/service/TagService.java +++ b/src/main/java/cn/rosercode/finance/service/TagService.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.dto.TagRequest; import cn.rosercode.finance.vo.TagVO; -import java.util.List; - /** * 标签服务接口,提供标签的增删改查操作。 * diff --git a/src/main/java/cn/rosercode/finance/service/TransactionService.java b/src/main/java/cn/rosercode/finance/service/TransactionService.java index 58e00d3..f0a01ed 100644 --- a/src/main/java/cn/rosercode/finance/service/TransactionService.java +++ b/src/main/java/cn/rosercode/finance/service/TransactionService.java @@ -1,5 +1,7 @@ package cn.rosercode.finance.service; +import java.util.List; + import cn.rosercode.finance.common.PageResult; import cn.rosercode.finance.dto.BatchCategoryRequest; import cn.rosercode.finance.dto.BatchTagRequest; @@ -8,8 +10,6 @@ import cn.rosercode.finance.dto.TransactionRequest; import cn.rosercode.finance.vo.TransactionExportVO; import cn.rosercode.finance.vo.TransactionVO; -import java.util.List; - /** * 交易记录服务接口,提供收支记录的分页查询、增删改操作。 */ diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiCategoryServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiCategoryServiceImpl.java index e641607..0fec0c0 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiCategoryServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiCategoryServiceImpl.java @@ -1,5 +1,12 @@ package cn.rosercode.finance.service.impl; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -9,12 +16,6 @@ import cn.rosercode.finance.service.AiCategoryService; import cn.rosercode.finance.service.AiConfigService; import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiCategoryResultVO; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; - -import java.util.List; /** * AI 生成分类服务实现类,根据自然语言描述生成分类名称、类型与图标。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiConfigServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiConfigServiceImpl.java index 506a45b..441e97c 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiConfigServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiConfigServiceImpl.java @@ -1,6 +1,10 @@ package cn.rosercode.finance.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -11,8 +15,6 @@ import cn.rosercode.finance.mapper.AiConfigMapper; import cn.rosercode.finance.service.AiConfigService; import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiConfigVO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** * AI 配置服务实现类,处理用户级 AI 配置的读写与连通性测试。 @@ -20,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional; * @date 2026-09-17 17:52:22 */ @Service +@Slf4j public class AiConfigServiceImpl implements AiConfigService { /** 默认 API 基础地址 */ @@ -89,6 +92,7 @@ public class AiConfigServiceImpl implements AiConfigService { } else { aiConfigMapper.updateById(config); } + log.info("保存 AI 配置: userId={}, enabled={}, model={}", userId, config.getEnabled(), config.getModel()); } /** @@ -121,10 +125,13 @@ public class AiConfigServiceImpl implements AiConfigService { // 失败也记录一次使用,便于排查问题 aiUsageService.recordFailure(userId, AiCallType.TEST, config.getModel(), 0, System.currentTimeMillis() - start, e.getMessage()); + log.warn("AI 连接测试失败: userId={}, model={}, error={}", userId, config.getModel(), e.getMessage()); throw e; } aiUsageService.recordSuccess(userId, AiCallType.TEST, config.getModel(), 0, response, System.currentTimeMillis() - start); + log.info("AI 连接测试成功: userId={}, model={}, 耗时={}ms", userId, config.getModel(), + System.currentTimeMillis() - start); if (isBlank(response.getContent())) { throw new BusinessException("连接成功但未返回内容"); } diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiDecorationServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiDecorationServiceImpl.java index d1a855b..093fd6a 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiDecorationServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiDecorationServiceImpl.java @@ -1,5 +1,12 @@ package cn.rosercode.finance.service.impl; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -8,12 +15,6 @@ import cn.rosercode.finance.enums.AiCallType; import cn.rosercode.finance.service.AiConfigService; import cn.rosercode.finance.service.AiDecorationService; import cn.rosercode.finance.service.AiUsageService; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; - -import java.util.regex.Pattern; /** * AI 装饰建议服务实现类,根据分类/标签名称调用模型生成 Emoji 与配色建议。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiQueryServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiQueryServiceImpl.java index 999007f..4a4a070 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiQueryServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiQueryServiceImpl.java @@ -1,6 +1,19 @@ package cn.rosercode.finance.service.impl; +import java.math.BigDecimal; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -14,18 +27,6 @@ import cn.rosercode.finance.service.AiConfigService; import cn.rosercode.finance.service.AiQueryService; import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiQueryResultVO; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; /** * AI 自然语言查询服务实现类,负责提示词组装、日期推算与名称映射。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiRecordServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiRecordServiceImpl.java index dd0fdb4..a096d87 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiRecordServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiRecordServiceImpl.java @@ -1,6 +1,19 @@ package cn.rosercode.finance.service.impl; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -14,18 +27,6 @@ import cn.rosercode.finance.service.AiConfigService; import cn.rosercode.finance.service.AiRecordService; import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiRecordResultVO; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; /** * AI 自然语言记账服务实现类,负责提示词组装、时间推算与名称映射。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiSuggestionServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiSuggestionServiceImpl.java index 9311b0d..8a78a72 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiSuggestionServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiSuggestionServiceImpl.java @@ -1,7 +1,25 @@ package cn.rosercode.finance.service.impl; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -22,23 +40,6 @@ import cn.rosercode.finance.service.AiSuggestionService; import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiNewTagVO; import cn.rosercode.finance.vo.AiSuggestionVO; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * AI 建议服务实现类,负责组装提示词、调用 DeepSeek、校验并应用建议。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiTagServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiTagServiceImpl.java index 69a1552..bf15e79 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiTagServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiTagServiceImpl.java @@ -1,5 +1,12 @@ package cn.rosercode.finance.service.impl; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.client.DeepSeekClient; import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.common.BusinessException; @@ -9,12 +16,6 @@ import cn.rosercode.finance.service.AiConfigService; import cn.rosercode.finance.service.AiTagService; import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiTagResultVO; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; - -import java.util.regex.Pattern; /** * AI 生成标签服务实现类,根据自然语言描述生成标签名称与颜色。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/AiUsageServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/AiUsageServiceImpl.java index 8390523..dba5f48 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/AiUsageServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/AiUsageServiceImpl.java @@ -1,6 +1,12 @@ package cn.rosercode.finance.service.impl; +import java.util.List; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.client.DeepSeekResponse; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.entity.AiUsage; @@ -10,12 +16,6 @@ import cn.rosercode.finance.service.AiUsageService; import cn.rosercode.finance.vo.AiUsageResultVO; import cn.rosercode.finance.vo.AiUsageSummaryVO; import cn.rosercode.finance.vo.AiUsageVO; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.stream.Collectors; /** * AI 使用记录服务实现类,负责写入调用明细并汇总查询。 @@ -23,10 +23,9 @@ import java.util.stream.Collectors; * @date 2026-09-17 18:02:23 */ @Service +@Slf4j public class AiUsageServiceImpl implements AiUsageService { - private static final Logger log = LoggerFactory.getLogger(AiUsageServiceImpl.class); - /** 最近明细条数上限 */ private static final int MAX_RECENT_LIMIT = 100; diff --git a/src/main/java/cn/rosercode/finance/service/impl/BillImportServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/BillImportServiceImpl.java index 21e4e77..a539b4b 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/BillImportServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/BillImportServiceImpl.java @@ -1,6 +1,24 @@ package cn.rosercode.finance.service.impl; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.dto.BillImportRequest; @@ -23,22 +41,6 @@ import cn.rosercode.finance.service.RuleService; import cn.rosercode.finance.vo.BillImportResultVO; import cn.rosercode.finance.vo.BillParseResultVO; import cn.rosercode.finance.vo.TagVO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; - -import java.io.IOException; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; /** * 账单导入服务实现类。 @@ -49,6 +51,7 @@ import java.util.stream.Collectors; * @date 2026-09-17 09:50:17 */ @Service +@Slf4j public class BillImportServiceImpl implements BillImportService { private final Map parserMap = new EnumMap<>(BillType.class); @@ -118,6 +121,8 @@ public class BillImportServiceImpl implements BillImportService { // 按规则引擎为每行生成分类与标签建议 applyRuleSuggestions(userId, rows); + log.info("账单解析完成: userId={}, billType={}, 总行数={}, 已存在交易号={}", + userId, billType, rows.size(), existing.size()); BillParseResultVO vo = new BillParseResultVO(); vo.setBillType(billType.name()); @@ -263,6 +268,8 @@ public class BillImportServiceImpl implements BillImportService { result.setSkipped(skipped); result.setFailed(failed); result.setMessages(messages); + log.info("账单导入完成: userId={}, billType={}, 总计={}, 成功={}, 跳过={}, 失败={}", + userId, request.getBillType(), items.size(), inserted.size(), skipped, failed); return result; } diff --git a/src/main/java/cn/rosercode/finance/service/impl/CategoryServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/CategoryServiceImpl.java index ff069dd..f25d19d 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/CategoryServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/CategoryServiceImpl.java @@ -1,6 +1,13 @@ package cn.rosercode.finance.service.impl; +import java.util.List; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.dto.CategoryRequest; @@ -8,16 +15,12 @@ import cn.rosercode.finance.entity.Category; import cn.rosercode.finance.mapper.CategoryMapper; import cn.rosercode.finance.service.CategoryService; import cn.rosercode.finance.vo.CategoryVO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.stream.Collectors; /** * 分类服务实现类,处理收支分类的增删改查业务逻辑。 */ @Service +@Slf4j public class CategoryServiceImpl implements CategoryService { private final CategoryMapper categoryMapper; @@ -73,6 +76,7 @@ public class CategoryServiceImpl implements CategoryService { // 排序值未提供时默认为0 category.setSortOrder(request.getSortOrder() != null ? request.getSortOrder() : 0); categoryMapper.insert(category); + log.info("新增分类: userId={}, id={}, name={}, type={}", userId, category.getId(), category.getName(), category.getType()); } /** @@ -95,6 +99,7 @@ public class CategoryServiceImpl implements CategoryService { category.setSortOrder(request.getSortOrder()); } categoryMapper.updateById(category); + log.info("更新分类: userId={}, id={}, name={}", userId, id, category.getName()); } /** @@ -111,6 +116,7 @@ public class CategoryServiceImpl implements CategoryService { throw new BusinessException("分类不存在"); } categoryMapper.deleteById(id); + log.info("删除分类: userId={}, id={}", userId, id); } } diff --git a/src/main/java/cn/rosercode/finance/service/impl/DatabaseServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/DatabaseServiceImpl.java index 7f84bf1..16f1514 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/DatabaseServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/DatabaseServiceImpl.java @@ -1,5 +1,21 @@ package cn.rosercode.finance.service.impl; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.config.datasource.DataSourceContextHolder; import cn.rosercode.finance.config.datasource.DataSourceType; @@ -10,22 +26,6 @@ import cn.rosercode.finance.config.datasource.SchemaInitializer; import cn.rosercode.finance.dto.DatabaseConfigRequest; import cn.rosercode.finance.service.DatabaseService; import cn.rosercode.finance.vo.DatabaseStatusVO; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import javax.sql.DataSource; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * 数据库配置服务实现类。 @@ -37,10 +37,9 @@ import java.util.regex.Pattern; * @date 2026-09-18 11:01:18 */ @Service +@Slf4j public class DatabaseServiceImpl implements DatabaseService { - private static final Logger log = LoggerFactory.getLogger(DatabaseServiceImpl.class); - /** 从 MySQL JDBC URL 中解析主机、端口与库名 */ private static final Pattern MYSQL_URL_PATTERN = Pattern.compile("jdbc:mysql://([^:/]+):(\\d+)/([^?]+)"); diff --git a/src/main/java/cn/rosercode/finance/service/impl/DefaultDataInitializer.java b/src/main/java/cn/rosercode/finance/service/impl/DefaultDataInitializer.java index 82548ad..4fa79d5 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/DefaultDataInitializer.java +++ b/src/main/java/cn/rosercode/finance/service/impl/DefaultDataInitializer.java @@ -1,12 +1,13 @@ package cn.rosercode.finance.service.impl; +import java.util.List; + +import org.springframework.stereotype.Component; + import cn.rosercode.finance.entity.Category; import cn.rosercode.finance.entity.Tag; import cn.rosercode.finance.mapper.CategoryMapper; import cn.rosercode.finance.mapper.TagMapper; -import org.springframework.stereotype.Component; - -import java.util.List; /** * 默认数据初始化器。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/LogServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/LogServiceImpl.java index 927a6f2..aa08134 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/LogServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/LogServiceImpl.java @@ -1,5 +1,20 @@ package cn.rosercode.finance.service.impl; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.common.PageResult; import cn.rosercode.finance.converter.VoConverter; @@ -13,21 +28,6 @@ import cn.rosercode.finance.util.AppLogUtils; import cn.rosercode.finance.vo.AppLogVO; import cn.rosercode.finance.vo.HttpLogVO; import cn.rosercode.finance.vo.LoginLogVO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; /** * 日志服务实现类,负责访问日志与登录日志的写入、分页查询以及应用运行日志的读取。 @@ -35,10 +35,9 @@ import java.util.regex.Pattern; * @date 2026-09-18 16:53:29 */ @Service +@Slf4j public class LogServiceImpl implements LogService { - private static final Logger log = LoggerFactory.getLogger(LogServiceImpl.class); - /** 分页每页最大条数 */ private static final int MAX_PAGE_SIZE = 100; diff --git a/src/main/java/cn/rosercode/finance/service/impl/NotificationChannelServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/NotificationChannelServiceImpl.java index 37e4ae7..0551e30 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/NotificationChannelServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/NotificationChannelServiceImpl.java @@ -1,5 +1,16 @@ package cn.rosercode.finance.service.impl; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.dto.NotificationChannelRequest; @@ -8,16 +19,6 @@ import cn.rosercode.finance.enums.NotificationChannelType; import cn.rosercode.finance.mapper.NotificationChannelMapper; import cn.rosercode.finance.service.NotificationChannelService; import cn.rosercode.finance.vo.NotificationChannelVO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; /** * 通知渠道服务实现类,处理用户消息推送渠道的增删改查与配置脱敏。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/NotificationConfigServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/NotificationConfigServiceImpl.java index eb5431a..a56726d 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/NotificationConfigServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/NotificationConfigServiceImpl.java @@ -1,5 +1,14 @@ package cn.rosercode.finance.service.impl; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.dto.NotificationConfigRequest; import cn.rosercode.finance.entity.NotificationConfig; @@ -7,14 +16,6 @@ import cn.rosercode.finance.enums.NotificationFrequency; import cn.rosercode.finance.mapper.NotificationConfigMapper; import cn.rosercode.finance.service.NotificationConfigService; import cn.rosercode.finance.vo.NotificationConfigVO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; /** * 通知配置服务实现类,处理用户级账单导入提醒周期的读写。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/NotificationServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/NotificationServiceImpl.java index 0105035..abbb4c5 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/NotificationServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/NotificationServiceImpl.java @@ -1,5 +1,16 @@ package cn.rosercode.finance.service.impl; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.config.app.AppConfigStore; import cn.rosercode.finance.converter.VoConverter; @@ -10,15 +21,6 @@ import cn.rosercode.finance.notification.NotificationSenderFactory; import cn.rosercode.finance.service.NotificationChannelService; import cn.rosercode.finance.service.NotificationService; import cn.rosercode.finance.vo.NotificationLogVO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Service; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; /** * 通知发送服务实现类,负责消息推送编排与发送日志记录。 @@ -26,6 +28,7 @@ import java.util.Map; * @date 2026-09-18 12:38:20 */ @Service +@Slf4j public class NotificationServiceImpl implements NotificationService { /** 日志正文最大长度 */ @@ -72,8 +75,10 @@ public class NotificationServiceImpl implements NotificationService { try { send(channel, TEST_TITLE, TEST_CONTENT); recordLog(userId, channel, true, "发送成功"); + log.info("测试通知发送成功: userId={}, channelId={}, type={}", userId, channelId, channel.getType()); } catch (RuntimeException e) { recordLog(userId, channel, false, e.getMessage()); + log.warn("测试通知发送失败: userId={}, channelId={}, error={}", userId, channelId, e.getMessage()); throw e; } } @@ -94,6 +99,7 @@ public class NotificationServiceImpl implements NotificationService { success++; } } + log.info("立即发送账单提醒: userId={}, 成功渠道={}/{}", userId, success, channels.size()); return success; } @@ -154,6 +160,8 @@ public class NotificationServiceImpl implements NotificationService { return true; } catch (RuntimeException e) { recordLog(userId, channel, false, e.getMessage()); + log.warn("通知发送失败: userId={}, channelId={}, type={}, error={}", + userId, channel.getId(), channel.getType(), e.getMessage()); return false; } } diff --git a/src/main/java/cn/rosercode/finance/service/impl/RegistrationServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/RegistrationServiceImpl.java index bb694f7..afd706f 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/RegistrationServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/RegistrationServiceImpl.java @@ -1,13 +1,13 @@ package cn.rosercode.finance.service.impl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + import cn.rosercode.finance.config.app.AppConfig; import cn.rosercode.finance.config.app.AppConfigStore; import cn.rosercode.finance.mapper.UserMapper; import cn.rosercode.finance.service.RegistrationService; import cn.rosercode.finance.vo.RegistrationStatusVO; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; /** * 注册管理服务实现类。 @@ -18,10 +18,9 @@ import org.springframework.stereotype.Service; * @date 2026-09-18 12:09:28 */ @Service +@Slf4j public class RegistrationServiceImpl implements RegistrationService { - private static final Logger log = LoggerFactory.getLogger(RegistrationServiceImpl.class); - private final UserMapper userMapper; private final AppConfigStore appConfigStore; diff --git a/src/main/java/cn/rosercode/finance/service/impl/RuleServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/RuleServiceImpl.java index d9a21ae..1085848 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/RuleServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/RuleServiceImpl.java @@ -1,6 +1,20 @@ package cn.rosercode.finance.service.impl; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.dto.RuleRequest; @@ -17,18 +31,6 @@ import cn.rosercode.finance.service.RuleMatchResult; import cn.rosercode.finance.service.RuleService; import cn.rosercode.finance.vo.RuleVO; import cn.rosercode.finance.vo.TagVO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; /** * 规则服务实现类,处理自定义分类与标签规则的增删改查与匹配业务逻辑。 @@ -39,6 +41,7 @@ import java.util.stream.Collectors; * @date 2026-09-18 17:24:59 */ @Service +@Slf4j public class RuleServiceImpl implements RuleService { private final RuleMapper ruleMapper; @@ -110,6 +113,7 @@ public class RuleServiceImpl implements RuleService { applyRequest(rule, request); ruleMapper.insert(rule); saveRuleTags(userId, rule.getId(), request.getTagIds()); + log.info("新增规则: userId={}, id={}, name={}", userId, rule.getId(), rule.getName()); } /** @@ -128,6 +132,7 @@ public class RuleServiceImpl implements RuleService { applyRequest(rule, request); ruleMapper.updateById(rule); saveRuleTags(userId, id, request.getTagIds()); + log.info("更新规则: userId={}, id={}, name={}", userId, id, rule.getName()); } /** @@ -145,6 +150,7 @@ public class RuleServiceImpl implements RuleService { ruleTagMapper.delete(new LambdaQueryWrapper() .eq(RuleTag::getRuleId, id)); ruleMapper.deleteById(id); + log.info("删除规则: userId={}, id={}", userId, id); } /** @@ -159,6 +165,7 @@ public class RuleServiceImpl implements RuleService { } rule.setEnabled(enabled == null ? Boolean.TRUE : enabled); ruleMapper.updateById(rule); + log.info("切换规则启用状态: userId={}, id={}, enabled={}", userId, id, rule.getEnabled()); } /** diff --git a/src/main/java/cn/rosercode/finance/service/impl/StatisticsServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/StatisticsServiceImpl.java index f488d05..5cc57ae 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/StatisticsServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/StatisticsServiceImpl.java @@ -1,6 +1,23 @@ package cn.rosercode.finance.service.impl; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.config.datasource.DataSourceContextHolder; import cn.rosercode.finance.config.datasource.DataSourceType; @@ -17,22 +34,6 @@ import cn.rosercode.finance.vo.MonthlyStatisticsVO; import cn.rosercode.finance.vo.StatisticsGroupVO; import cn.rosercode.finance.vo.StatisticsSummaryVO; import cn.rosercode.finance.vo.TrendStatisticsVO; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.YearMonth; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; /** * 统计服务实现类,提供月度收支汇总、分类占比分析、趋势统计、概览指标及通用分组统计。 diff --git a/src/main/java/cn/rosercode/finance/service/impl/TagServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/TagServiceImpl.java index 50b56e1..8889c77 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/TagServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/TagServiceImpl.java @@ -1,6 +1,13 @@ package cn.rosercode.finance.service.impl; +import java.util.List; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.dto.TagRequest; @@ -12,11 +19,6 @@ import cn.rosercode.finance.mapper.TagMapper; import cn.rosercode.finance.mapper.TransactionTagMapper; import cn.rosercode.finance.service.TagService; import cn.rosercode.finance.vo.TagVO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.stream.Collectors; /** * 标签服务实现类,处理标签的增删改查业务逻辑。 @@ -24,6 +26,7 @@ import java.util.stream.Collectors; * @date 2026-09-17 16:26:56 */ @Service +@Slf4j public class TagServiceImpl implements TagService { private final TagMapper tagMapper; @@ -74,6 +77,7 @@ public class TagServiceImpl implements TagService { // 排序值未提供时默认为0 tag.setSortOrder(request.getSortOrder() != null ? request.getSortOrder() : 0); tagMapper.insert(tag); + log.info("新增标签: userId={}, id={}, name={}", userId, tag.getId(), tag.getName()); } /** @@ -101,6 +105,7 @@ public class TagServiceImpl implements TagService { tag.setSortOrder(request.getSortOrder()); } tagMapper.updateById(tag); + log.info("更新标签: userId={}, id={}, name={}", userId, id, tag.getName()); } /** @@ -120,6 +125,7 @@ public class TagServiceImpl implements TagService { ruleTagMapper.delete(new LambdaQueryWrapper() .eq(RuleTag::getTagId, id)); tagMapper.deleteById(id); + log.info("删除标签: userId={}, id={}", userId, id); } } diff --git a/src/main/java/cn/rosercode/finance/service/impl/TransactionServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/TransactionServiceImpl.java index 7f56300..49883b2 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/TransactionServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/TransactionServiceImpl.java @@ -1,8 +1,22 @@ package cn.rosercode.finance.service.impl; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.common.PageResult; import cn.rosercode.finance.converter.VoConverter; @@ -23,23 +37,12 @@ import cn.rosercode.finance.service.TransactionService; import cn.rosercode.finance.vo.TagVO; import cn.rosercode.finance.vo.TransactionExportVO; import cn.rosercode.finance.vo.TransactionVO; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; /** * 交易记录服务实现类,处理收支记录的分页查询、创建、更新和删除等业务逻辑。 */ @Service +@Slf4j public class TransactionServiceImpl implements TransactionService { /** 备注被隐藏时在导出文件中使用的占位符 */ @@ -191,6 +194,8 @@ public class TransactionServiceImpl implements TransactionService { transactionMapper.insert(transaction); // 同步标签关联 saveTransactionTags(userId, transaction.getId(), request.getTagIds()); + log.info("新增交易记录: userId={}, id={}, type={}, amount={}", + userId, transaction.getId(), transaction.getType(), transaction.getAmount()); } /** @@ -225,6 +230,7 @@ public class TransactionServiceImpl implements TransactionService { transactionMapper.updateById(transaction); // 全量替换标签关联 saveTransactionTags(userId, id, request.getTagIds()); + log.info("更新交易记录: userId={}, id={}", userId, id); } /** @@ -243,6 +249,7 @@ public class TransactionServiceImpl implements TransactionService { transactionTagMapper.delete(new LambdaQueryWrapper() .eq(TransactionTag::getTransactionId, id)); transactionMapper.deleteById(id); + log.info("删除交易记录: userId={}, id={}", userId, id); } /** @@ -270,7 +277,9 @@ public class TransactionServiceImpl implements TransactionService { // 先清理标签关联,再批量删除记录 transactionTagMapper.delete(new LambdaQueryWrapper() .in(TransactionTag::getTransactionId, ownedIds)); - return transactionMapper.deleteByIds(ownedIds); + int deleted = transactionMapper.deleteByIds(ownedIds); + log.info("批量删除交易记录: userId={}, 请求={}, 实际={}", userId, ids.size(), deleted); + return deleted; } /** @@ -287,6 +296,7 @@ public class TransactionServiceImpl implements TransactionService { } transaction.setIncludeInStats(includeInStats == null ? Boolean.TRUE : includeInStats); transactionMapper.updateById(transaction); + log.info("切换计入统计标记: userId={}, id={}, includeInStats={}", userId, id, transaction.getIncludeInStats()); } /** @@ -303,6 +313,7 @@ public class TransactionServiceImpl implements TransactionService { } transaction.setRemarkHidden(remarkHidden == null ? Boolean.FALSE : remarkHidden); transactionMapper.updateById(transaction); + log.info("切换备注隐藏标记: userId={}, id={}, remarkHidden={}", userId, id, transaction.getRemarkHidden()); } /** @@ -349,6 +360,7 @@ public class TransactionServiceImpl implements TransactionService { int updated = 0; updated += batchUpdate(userId, expenseIds, expenseCategory, includeInStats); updated += batchUpdate(userId, incomeIds, incomeCategory, includeInStats); + log.info("批量设置分类: userId={}, 请求={}, 实际={}", userId, ids.size(), updated); return updated; } @@ -431,6 +443,8 @@ public class TransactionServiceImpl implements TransactionService { } } } + log.info("批量设置标签: userId={}, 记录={}, 添加={}, 移除={}", + userId, validIds.size(), addTagIds.size(), removeTagIds.size()); return validIds.size(); } diff --git a/src/main/java/cn/rosercode/finance/service/impl/UserServiceImpl.java b/src/main/java/cn/rosercode/finance/service/impl/UserServiceImpl.java index dc510a4..50d0410 100644 --- a/src/main/java/cn/rosercode/finance/service/impl/UserServiceImpl.java +++ b/src/main/java/cn/rosercode/finance/service/impl/UserServiceImpl.java @@ -1,6 +1,14 @@ package cn.rosercode.finance.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + import cn.rosercode.finance.common.BusinessException; import cn.rosercode.finance.converter.VoConverter; import cn.rosercode.finance.dto.LoginRequest; @@ -15,17 +23,12 @@ import cn.rosercode.finance.util.JwtUtil; import cn.rosercode.finance.util.RequestUtils; import cn.rosercode.finance.vo.LoginVO; import cn.rosercode.finance.vo.UserVO; -import jakarta.servlet.http.HttpServletRequest; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; /** * 用户服务实现类,处理用户登录认证、注册和信息查询等业务逻辑。 */ @Service +@Slf4j public class UserServiceImpl implements UserService { private final UserMapper userMapper; @@ -58,12 +61,14 @@ public class UserServiceImpl implements UserService { .eq(User::getUsername, request.getUsername())); if (user == null) { + log.warn("用户登录失败,用户名不存在: username={}", request.getUsername()); recordLoginLog(null, request.getUsername(), false, "用户名或密码错误"); throw new BusinessException("用户名或密码错误"); } // 校验密码是否匹配 if (!passwordEncoder.matches(request.getPassword(), user.getPasswordHash())) { + log.warn("用户登录失败,密码错误: username={}", request.getUsername()); recordLoginLog(user.getId(), request.getUsername(), false, "用户名或密码错误"); throw new BusinessException("用户名或密码错误"); } @@ -71,6 +76,7 @@ public class UserServiceImpl implements UserService { // 生成 JWT Token String token = jwtUtil.generateToken(user.getId(), user.getUsername()); recordLoginLog(user.getId(), user.getUsername(), true, "登录成功"); + log.info("用户登录成功: userId={}, username={}", user.getId(), user.getUsername()); LoginVO vo = new LoginVO(); vo.setToken(token); @@ -106,6 +112,7 @@ public class UserServiceImpl implements UserService { // 如果未提供昵称,则使用用户名作为默认昵称 user.setNickname(request.getNickname() != null ? request.getNickname() : request.getUsername()); userMapper.insert(user); + log.info("用户注册成功: userId={}, username={}", user.getId(), user.getUsername()); // 为新用户初始化默认分类与标签 defaultDataInitializer.initializeForUser(user.getId()); diff --git a/src/main/java/cn/rosercode/finance/util/JwtUtil.java b/src/main/java/cn/rosercode/finance/util/JwtUtil.java index dd307e0..4228954 100644 --- a/src/main/java/cn/rosercode/finance/util/JwtUtil.java +++ b/src/main/java/cn/rosercode/finance/util/JwtUtil.java @@ -1,14 +1,15 @@ package cn.rosercode.finance.util; -import cn.rosercode.finance.config.JwtConfig; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import javax.crypto.SecretKey; + import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.stereotype.Component; -import javax.crypto.SecretKey; -import java.nio.charset.StandardCharsets; -import java.util.Date; +import cn.rosercode.finance.config.JwtConfig; /** * JWT 工具类。 diff --git a/src/main/java/cn/rosercode/finance/util/UserContext.java b/src/main/java/cn/rosercode/finance/util/UserContext.java new file mode 100644 index 0000000..0b0ac98 --- /dev/null +++ b/src/main/java/cn/rosercode/finance/util/UserContext.java @@ -0,0 +1,59 @@ +package cn.rosercode.finance.util; + +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import cn.rosercode.finance.common.BusinessException; + +/** + * 当前登录用户上下文工具类。 + * + *

从 Spring 请求上下文中读取 JWT 拦截器写入的用户 ID 与响应对象, + * 使控制器方法无需再声明 {@code HttpServletRequest}、{@code HttpServletResponse} 参数。

+ * + * @date 2026-09-19 14:36:40 + */ +public final class UserContext { + + private UserContext() { + } + + /** + * 获取当前登录用户 ID。 + * + * @return 当前登录用户 ID + * @throws BusinessException 无请求上下文或未登录时抛出 + */ + public static Long getUserId() { + ServletRequestAttributes attributes = currentAttributes(); + Object userId = attributes == null ? null : attributes.getRequest().getAttribute("userId"); + if (userId instanceof Long id) { + return id; + } + throw new BusinessException("未登录"); + } + + /** + * 获取当前线程绑定的 HTTP 响应。 + * + * @return 当前 HTTP 响应 + * @throws BusinessException 无请求上下文时抛出 + */ + public static HttpServletResponse getResponse() { + ServletRequestAttributes attributes = currentAttributes(); + if (attributes == null || attributes.getResponse() == null) { + throw new BusinessException("无法获取当前响应"); + } + return attributes.getResponse(); + } + + /** + * 获取当前线程绑定的 Servlet 请求属性。 + * + * @return 请求属性,非 Web 线程时返回 null + */ + private static ServletRequestAttributes currentAttributes() { + return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + } +} diff --git a/src/main/java/cn/rosercode/finance/vo/AiQueryResultVO.java b/src/main/java/cn/rosercode/finance/vo/AiQueryResultVO.java index c6b5f3a..b728731 100644 --- a/src/main/java/cn/rosercode/finance/vo/AiQueryResultVO.java +++ b/src/main/java/cn/rosercode/finance/vo/AiQueryResultVO.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; import java.util.List; +import lombok.Data; + /** * AI 自然语言查询解析结果视图对象,字段与交易查询条件对应。 * diff --git a/src/main/java/cn/rosercode/finance/vo/AiRecordResultVO.java b/src/main/java/cn/rosercode/finance/vo/AiRecordResultVO.java index 8ce81ee..daac77a 100644 --- a/src/main/java/cn/rosercode/finance/vo/AiRecordResultVO.java +++ b/src/main/java/cn/rosercode/finance/vo/AiRecordResultVO.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; import java.util.List; +import lombok.Data; + /** * AI 自然语言记账解析结果视图对象,字段与交易记录表单对应。 * diff --git a/src/main/java/cn/rosercode/finance/vo/AiSuggestionVO.java b/src/main/java/cn/rosercode/finance/vo/AiSuggestionVO.java index 439cd9a..e26d807 100644 --- a/src/main/java/cn/rosercode/finance/vo/AiSuggestionVO.java +++ b/src/main/java/cn/rosercode/finance/vo/AiSuggestionVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.util.List; +import lombok.Data; + /** * AI 建议视图对象,描述某条交易记录的分类与标签建议。 * diff --git a/src/main/java/cn/rosercode/finance/vo/AiUsageResultVO.java b/src/main/java/cn/rosercode/finance/vo/AiUsageResultVO.java index 36f03ac..f30f0d9 100644 --- a/src/main/java/cn/rosercode/finance/vo/AiUsageResultVO.java +++ b/src/main/java/cn/rosercode/finance/vo/AiUsageResultVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.util.List; +import lombok.Data; + /** * AI 使用情况视图对象,包含累计汇总与最近调用明细。 * diff --git a/src/main/java/cn/rosercode/finance/vo/AiUsageVO.java b/src/main/java/cn/rosercode/finance/vo/AiUsageVO.java index 0f4e5b5..9f61699 100644 --- a/src/main/java/cn/rosercode/finance/vo/AiUsageVO.java +++ b/src/main/java/cn/rosercode/finance/vo/AiUsageVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * AI 使用明细视图对象,描述一次调用的用量与结果。 * diff --git a/src/main/java/cn/rosercode/finance/vo/AppLogVO.java b/src/main/java/cn/rosercode/finance/vo/AppLogVO.java index b33553d..442f1de 100644 --- a/src/main/java/cn/rosercode/finance/vo/AppLogVO.java +++ b/src/main/java/cn/rosercode/finance/vo/AppLogVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.util.List; +import lombok.Data; + /** * 应用运行日志视图对象,用于向前端展示后端日志文件内容。 * diff --git a/src/main/java/cn/rosercode/finance/vo/BillImportResultVO.java b/src/main/java/cn/rosercode/finance/vo/BillImportResultVO.java index 114987b..efec422 100644 --- a/src/main/java/cn/rosercode/finance/vo/BillImportResultVO.java +++ b/src/main/java/cn/rosercode/finance/vo/BillImportResultVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.util.List; +import lombok.Data; + /** * 账单导入结果视图对象,用于「后导入」阶段返回导入统计。 * diff --git a/src/main/java/cn/rosercode/finance/vo/BillParseResultVO.java b/src/main/java/cn/rosercode/finance/vo/BillParseResultVO.java index 2164a93..d670af0 100644 --- a/src/main/java/cn/rosercode/finance/vo/BillParseResultVO.java +++ b/src/main/java/cn/rosercode/finance/vo/BillParseResultVO.java @@ -1,9 +1,10 @@ package cn.rosercode.finance.vo; -import cn.rosercode.finance.importer.ParsedBillRow; +import java.util.List; + import lombok.Data; -import java.util.List; +import cn.rosercode.finance.importer.ParsedBillRow; /** * 账单解析结果视图对象,用于「先解析」阶段向前端返回预览数据。 diff --git a/src/main/java/cn/rosercode/finance/vo/CategoryStatisticsVO.java b/src/main/java/cn/rosercode/finance/vo/CategoryStatisticsVO.java index 79352f2..eb139fc 100644 --- a/src/main/java/cn/rosercode/finance/vo/CategoryStatisticsVO.java +++ b/src/main/java/cn/rosercode/finance/vo/CategoryStatisticsVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; +import lombok.Data; + /** * 分类统计视图对象,用于展示各分类的收支占比情况 */ diff --git a/src/main/java/cn/rosercode/finance/vo/CategoryVO.java b/src/main/java/cn/rosercode/finance/vo/CategoryVO.java index 2c2d8f9..b563b88 100644 --- a/src/main/java/cn/rosercode/finance/vo/CategoryVO.java +++ b/src/main/java/cn/rosercode/finance/vo/CategoryVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * 分类视图对象,用于向前端返回分类信息 */ diff --git a/src/main/java/cn/rosercode/finance/vo/DatabaseStatusVO.java b/src/main/java/cn/rosercode/finance/vo/DatabaseStatusVO.java index ab592d7..4de3969 100644 --- a/src/main/java/cn/rosercode/finance/vo/DatabaseStatusVO.java +++ b/src/main/java/cn/rosercode/finance/vo/DatabaseStatusVO.java @@ -1,8 +1,9 @@ package cn.rosercode.finance.vo; -import cn.rosercode.finance.config.datasource.DataSourceType; import lombok.Data; +import cn.rosercode.finance.config.datasource.DataSourceType; + /** * 数据库状态视图对象,用于向前端展示当前数据库配置情况。 * diff --git a/src/main/java/cn/rosercode/finance/vo/HttpLogVO.java b/src/main/java/cn/rosercode/finance/vo/HttpLogVO.java index 37f4534..6203b24 100644 --- a/src/main/java/cn/rosercode/finance/vo/HttpLogVO.java +++ b/src/main/java/cn/rosercode/finance/vo/HttpLogVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * HTTP 访问日志视图对象,用于向前端展示接口调用记录。 * diff --git a/src/main/java/cn/rosercode/finance/vo/LoginLogVO.java b/src/main/java/cn/rosercode/finance/vo/LoginLogVO.java index cd3300e..acae624 100644 --- a/src/main/java/cn/rosercode/finance/vo/LoginLogVO.java +++ b/src/main/java/cn/rosercode/finance/vo/LoginLogVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * 登录日志视图对象,用于向前端展示登录尝试记录。 * diff --git a/src/main/java/cn/rosercode/finance/vo/MonthlyStatisticsVO.java b/src/main/java/cn/rosercode/finance/vo/MonthlyStatisticsVO.java index 96e8643..5e3d732 100644 --- a/src/main/java/cn/rosercode/finance/vo/MonthlyStatisticsVO.java +++ b/src/main/java/cn/rosercode/finance/vo/MonthlyStatisticsVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; +import lombok.Data; + /** * 月度统计视图对象,用于展示某月的收支汇总信息 */ diff --git a/src/main/java/cn/rosercode/finance/vo/NotificationChannelVO.java b/src/main/java/cn/rosercode/finance/vo/NotificationChannelVO.java index 50e4b1a..52b1288 100644 --- a/src/main/java/cn/rosercode/finance/vo/NotificationChannelVO.java +++ b/src/main/java/cn/rosercode/finance/vo/NotificationChannelVO.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; import java.util.Map; +import lombok.Data; + /** * 通知渠道视图对象,用于向前端返回渠道信息(敏感字段已脱敏)。 * diff --git a/src/main/java/cn/rosercode/finance/vo/NotificationConfigVO.java b/src/main/java/cn/rosercode/finance/vo/NotificationConfigVO.java index 91dcc04..3840e7d 100644 --- a/src/main/java/cn/rosercode/finance/vo/NotificationConfigVO.java +++ b/src/main/java/cn/rosercode/finance/vo/NotificationConfigVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * 通知配置视图对象,用于向前端返回账单导入提醒的周期设置。 * diff --git a/src/main/java/cn/rosercode/finance/vo/NotificationLogVO.java b/src/main/java/cn/rosercode/finance/vo/NotificationLogVO.java index 487e043..fc7adfc 100644 --- a/src/main/java/cn/rosercode/finance/vo/NotificationLogVO.java +++ b/src/main/java/cn/rosercode/finance/vo/NotificationLogVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * 通知日志视图对象,用于向前端展示历史发送结果。 * diff --git a/src/main/java/cn/rosercode/finance/vo/RuleVO.java b/src/main/java/cn/rosercode/finance/vo/RuleVO.java index 518bc08..f022b51 100644 --- a/src/main/java/cn/rosercode/finance/vo/RuleVO.java +++ b/src/main/java/cn/rosercode/finance/vo/RuleVO.java @@ -1,11 +1,11 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; +import lombok.Data; + /** * 规则视图对象,用于向前端返回自定义分类与标签规则信息。 * diff --git a/src/main/java/cn/rosercode/finance/vo/StatisticsGroupVO.java b/src/main/java/cn/rosercode/finance/vo/StatisticsGroupVO.java index 09f3a8c..fd8ab88 100644 --- a/src/main/java/cn/rosercode/finance/vo/StatisticsGroupVO.java +++ b/src/main/java/cn/rosercode/finance/vo/StatisticsGroupVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; +import lombok.Data; + /** * 分组统计视图对象,用于展示按某一维度分组后的收支数据。 * diff --git a/src/main/java/cn/rosercode/finance/vo/StatisticsSummaryVO.java b/src/main/java/cn/rosercode/finance/vo/StatisticsSummaryVO.java index c9b5232..38e711a 100644 --- a/src/main/java/cn/rosercode/finance/vo/StatisticsSummaryVO.java +++ b/src/main/java/cn/rosercode/finance/vo/StatisticsSummaryVO.java @@ -1,10 +1,10 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; import java.time.LocalDateTime; +import lombok.Data; + /** * 统计概览视图对象,用于展示指定时间范围内的收支汇总与派生指标。 * diff --git a/src/main/java/cn/rosercode/finance/vo/TagVO.java b/src/main/java/cn/rosercode/finance/vo/TagVO.java index 94f76a6..5f161a4 100644 --- a/src/main/java/cn/rosercode/finance/vo/TagVO.java +++ b/src/main/java/cn/rosercode/finance/vo/TagVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * 标签视图对象,用于向前端返回标签信息。 * diff --git a/src/main/java/cn/rosercode/finance/vo/TransactionVO.java b/src/main/java/cn/rosercode/finance/vo/TransactionVO.java index c832666..8055da4 100644 --- a/src/main/java/cn/rosercode/finance/vo/TransactionVO.java +++ b/src/main/java/cn/rosercode/finance/vo/TransactionVO.java @@ -1,11 +1,11 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; +import lombok.Data; + /** * 交易记录视图对象,用于向前端返回交易详情(包含关联的分类信息) */ diff --git a/src/main/java/cn/rosercode/finance/vo/TrendStatisticsVO.java b/src/main/java/cn/rosercode/finance/vo/TrendStatisticsVO.java index a1c3a65..3c7ef41 100644 --- a/src/main/java/cn/rosercode/finance/vo/TrendStatisticsVO.java +++ b/src/main/java/cn/rosercode/finance/vo/TrendStatisticsVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.math.BigDecimal; +import lombok.Data; + /** * 趋势统计视图对象,用于展示按日/月的收支趋势变化 */ diff --git a/src/main/java/cn/rosercode/finance/vo/UserVO.java b/src/main/java/cn/rosercode/finance/vo/UserVO.java index 83070a6..a07244c 100644 --- a/src/main/java/cn/rosercode/finance/vo/UserVO.java +++ b/src/main/java/cn/rosercode/finance/vo/UserVO.java @@ -1,9 +1,9 @@ package cn.rosercode.finance.vo; -import lombok.Data; - import java.time.LocalDateTime; +import lombok.Data; + /** * 用户视图对象,用于向前端返回用户基本信息(不包含敏感数据) */