265 lines
16 KiB
Markdown
265 lines
16 KiB
Markdown
# 会话记录:货币格式化功能
|
||
|
||
## 元数据
|
||
- 需求编号:2026-01-21-002-05
|
||
- 创建时间:2026-01-25
|
||
- 创建人:SSOT 架构师
|
||
- 状态:进行中
|
||
- 父需求:2026-01-21-002-项目国际化需求
|
||
|
||
## 执行阶段
|
||
|
||
### 阶段 1:需求定义
|
||
- **状态**:已完成
|
||
- **生成文档**:[需求文档](../requirements/2026-01-21-002-05-货币格式化需求.md)
|
||
- **关键决策**:
|
||
- 扩展 sys_user 表,添加 currency_code 字段
|
||
- 支持用户级别的货币偏好覆盖系统默认设置
|
||
- 在配置文件中设置系统默认货币
|
||
- 在后端返回数据时进行格式化,通过 AOP 在 Service 层统一处理
|
||
- 需要支持多货币显示
|
||
- 需要实时汇率转换
|
||
- 后端使用 java.text.DecimalFormat
|
||
- 不考虑性能、安全性等问题
|
||
|
||
### 阶段 2:方案设计
|
||
- **状态**:已完成
|
||
- **生成文档**:[设计文档](../design/2026-01-21-002-05-货币格式化设计.md)
|
||
- **关键设计决策**:
|
||
- 使用 Java DecimalFormat 进行货币格式化
|
||
- 使用 Redis 缓存汇率数据,提高性能
|
||
- 货币格式化在 Service 层进行,使用 AOP 切面拦截响应
|
||
- 货币优先级:用户 > 系统
|
||
- 汇率数据存储在数据库中,支持手动更新和 API 自动更新
|
||
- 所有金额字段使用 BigDecimal 类型
|
||
- 缓存策略:汇率缓存(TTL = 60 分钟)、货币列表缓存(TTL = 24 小时)、默认货币缓存(TTL = 24 小时)
|
||
|
||
### 阶段 3:方案决策
|
||
- **状态**:已完成
|
||
- **生成文档**:[决策记录](../decisions/adr/2026-01-25-002-05-ADR-货币格式化技术选型.md)
|
||
- **关键决策**:
|
||
- **决策 1:货币格式化技术选择** - Java DecimalFormat
|
||
- 理由:Java 内置、无需额外依赖、功能完整、性能优秀、成熟稳定、与框架兼容
|
||
- 放弃方案:Joda-Money 库(需要额外依赖)、自定义格式化实现(维护成本高)
|
||
- **决策 2:缓存策略选择** - Redis 缓存
|
||
- 理由:已集成、性能优秀、分布式支持、自动过期、数据结构丰富、支持持久化
|
||
- 缓存策略:汇率缓存(TTL = 60 分钟)、货币列表缓存(TTL = 24 小时)、默认货币缓存(TTL = 24 小时)
|
||
- 放弃方案:Caffeine 本地缓存(无法在分布式环境下共享)、数据库缓存(性能较差、无法自动过期)
|
||
- **决策 3:货币格式化层选择** - Service 层格式化 + AOP 切面拦截
|
||
- 理由:统一处理、AOP 切面自动格式化、业务逻辑分离、易于维护、性能优化
|
||
- 实现方案:使用 @Around 切面拦截 Service 方法返回值,提供 @CurrencyFormat 注解
|
||
- 放弃方案:Controller 层格式化(职责过重、代码重复)、数据库层格式化(违反分层架构、兼容性差)
|
||
- **决策 4:货币优先级策略选择** - 双级优先级策略(用户 > 系统)
|
||
- 理由:灵活性高、用户体验好、系统默认、易于扩展
|
||
- 优先级规则:用户货币 > 系统货币 > 硬编码默认(CNY)
|
||
- 放弃方案:单级优先级(仅用户货币/仅系统货币)- 灵活性不足
|
||
|
||
### 阶段 4:数据库结构
|
||
- **状态**:已完成
|
||
- **生成文档**:[SQL 脚本](../sql/2026-01-25-002-05-货币格式化.sql)
|
||
- **数据库变更**:
|
||
- **创建新表**:sys_exchange_rate(汇率表)
|
||
- 字段:id、from_currency、to_currency、rate、source、create_by、create_time、update_by、update_time、remark
|
||
- 索引:主键索引(id)、唯一索引(from_currency, to_currency)、普通索引(source)
|
||
- **修改现有表**:sys_user(用户表)
|
||
- 新增字段:currency_code(货币代码),默认值为 CNY
|
||
- 新增索引:idx_currency_code(currency_code 字段)
|
||
- **插入初始数据**:
|
||
- 插入常用货币配置(CNY、USD、EUR、GBP、JPY)
|
||
|
||
### 阶段 5:提示词生成
|
||
- **状态**:已完成
|
||
- **生成文档**:[提示词文档](../prompts/2026-01-25-002-05-prompt-货币格式化功能.md)
|
||
- **提示词内容摘要**:
|
||
- **引用真源**:需求文档、设计文档、决策记录、SQL 脚本
|
||
- **需求描述**:货币格式化功能、汇率管理功能、货币优先级策略、缓存管理、自动货币格式化
|
||
- **设计方案**:Java DecimalFormat、Redis、MySQL 8.3.0、Spring Boot 3.5.7 + 若依框架、分层架构
|
||
- **输出格式要求**:
|
||
- Entity 层:SysExchangeRate.java
|
||
- 修改现有文件:SysUser.java(添加 currency_code 字段)
|
||
- Utils 层:CurrencyUtils.java
|
||
- 注解:@CurrencyFormat.java
|
||
- Aspect 层:CurrencyFormatAspect.java(拦截 Service 方法返回值)
|
||
- Mapper 层:SysExchangeRateMapper.java、SysExchangeRateMapper.xml
|
||
- Service 层:ISysExchangeRateService.java、SysExchangeRateServiceImpl.java
|
||
- Controller 层:SysExchangeRateController.java
|
||
- 单元测试:CurrencyUtilsTest.java、SysExchangeRateServiceTest.java
|
||
- **代码规范要求**:命名规范、注释规范、代码格式、导入规范
|
||
- **测试要求**:单元测试覆盖率 ≥ 80%、测试用例场景、测试框架、测试用例命名规范、测试数据
|
||
- **注意事项**:货币格式化、缓存、权限控制、日志记录、异常处理、性能优化、安全、AOP 切面
|
||
|
||
### 阶段 6:代码生成
|
||
- **状态**:已完成
|
||
- **生成文档**:[代码文档](../reference-code/2026-01-25-002-05-code-货币格式化功能.md)、[实施方案](../implementation/2026-01-25-002-05-implementation-货币格式化功能.md)
|
||
- **代码生成情况**:
|
||
- **代码生成器生成**:
|
||
- SysExchangeRate.java(实体类)
|
||
- SysExchangeRateMapper.java(Mapper 接口)
|
||
- SysExchangeRateMapper.xml(Mapper XML)
|
||
- SysExchangeRateService.java(Service 接口基础方法)
|
||
- SysExchangeRateServiceImpl.java(Service 实现类基础方法)
|
||
- SysExchangeRateController.java(Controller 类基础接口)
|
||
- **手动生成/扩展**:
|
||
- ISysExchangeRateService.java:扩展方法(getExchangeRate、convert、batchConvert、updateExchangeRate、updateExchangeRateFromApi、getAvailableCurrencies、getDefaultCurrency)
|
||
- SysExchangeRateServiceImpl.java:实现扩展方法,包含 Redis 缓存逻辑
|
||
- CurrencyUtils.java:货币格式化工具类(format、convert、isValidCurrencyCode、getPattern、getSymbol)
|
||
- CurrencyConstants.java:货币常量类(货币代码、货币符号、货币格式化模式)
|
||
- @CurrencyFormat.java:货币格式化注解
|
||
- CurrencyFormatAspect.java:货币格式化切面(拦截 Service 方法返回值)
|
||
- SysUser.java:添加 currency_code 字段
|
||
- SysUserController.java:添加用户货币偏好管理接口
|
||
- SysExchangeRateDto.java:添加汇率转换相关字段
|
||
- CacheConstants.java:添加汇率相关缓存常量
|
||
- **关键实现**:
|
||
- 使用 AOP 切面拦截 Service 方法返回值进行货币格式化
|
||
- Redis 缓存策略:汇率缓存 60 分钟,货币列表缓存 24 小时,默认货币缓存 24 小时
|
||
- 递归格式化复杂对象中的 BigDecimal 字段
|
||
- 支持多货币显示,自动转换并显示多种货币
|
||
- 异常处理:无效货币使用默认货币,null 值安全处理
|
||
- **修复的问题**:
|
||
- CurrencyFormatAspect.java 中的 SYS_CONFIG 常量引用错误(修改为 SYS_CONFIG_KEY)
|
||
- **补充的接口**:
|
||
- GET /system/user/currency:获取当前用户货币偏好
|
||
- POST /system/user/switchCurrency:切换用户货币偏好(已添加 @Log 注解记录审计日志)
|
||
- POST /system/exchangeRate/convert:汇率转换
|
||
- POST /system/exchangeRate/batchConvert:批量汇率转换
|
||
- POST /system/exchangeRate/update:更新汇率(手动)
|
||
- POST /system/exchangeRate/updateFromApi:更新汇率(API)
|
||
- **需求实现情况**:
|
||
- ✅ 功能 1:货币符号支持 - 已实现
|
||
- ✅ 功能 2:小数格式化 - 已实现
|
||
- ✅ 功能 3:千分位分隔符 - 已实现(逗号分隔符)
|
||
- ⚠️ 功能 4:自动格式选择 - 已实现(部分)
|
||
- ⚠️ 功能 5:自定义格式 - 已实现(部分)
|
||
- ✅ 功能 6:多货币显示 - 已实现
|
||
- ✅ 功能 7:AOP 自动格式化 - 已实现
|
||
- ✅ 功能 8:用户货币偏好 - 已实现
|
||
- ✅ 功能 9:系统默认货币 - 已实现
|
||
- ✅ 功能 10:汇率管理 - 已实现
|
||
- ✅ 功能 11:汇率转换 - 已实现
|
||
- ✅ 功能 12:汇率缓存 - 已实现
|
||
- ⚠️ 功能 13:货币优先级 - 已实现(部分,缺少租户货币支持)
|
||
- **未实现的需求**:
|
||
- 空格分隔符支持(功能 3 的部分需求)
|
||
- 自定义千分位分隔符(功能 5 的部分需求)
|
||
- 自定义货币符号(功能 5 的部分需求)
|
||
- 租户货币支持(功能 13 的部分需求)
|
||
- 单元测试
|
||
- 性能测试
|
||
|
||
### 阶段 7:会话记录
|
||
- **状态**:已完成
|
||
- **生成文档**:当前文档(2026-01-25-002-05-session.md)
|
||
- **更新内容**:
|
||
- 更新阶段 6 的代码生成情况
|
||
- 记录补充的接口信息
|
||
- 记录需求实现情况
|
||
- 记录未实现的需求
|
||
|
||
### 阶段 8:变更日志
|
||
- **状态**:待开始
|
||
- **生成文档**:待生成
|
||
|
||
### 阶段 9:复盘与接口
|
||
- **状态**:待开始
|
||
- **生成文档**:待生成
|
||
|
||
### 阶段 10:代码提交
|
||
- **状态**:待开始
|
||
- **生成文档**:待生成
|
||
|
||
## 关键设计决策
|
||
|
||
### 技术选型
|
||
1. **货币格式化技术**:Java DecimalFormat
|
||
- 理由:Java 内置,无需引入额外依赖;支持自定义格式化模式;性能优秀,格式化时间 < 10ms;成熟稳定,社区支持良好
|
||
|
||
2. **缓存技术**:Redis
|
||
- 理由:项目已集成 Redis,无需额外配置;性能优秀,响应时间 < 1ms;支持分布式部署;支持自动过期机制;丰富的数据结构支持
|
||
|
||
3. **数据库技术**:MySQL 8.3.0
|
||
- 理由:项目现有数据库;支持 BigDecimal 类型;性能优秀,支持高并发;事务支持完善
|
||
|
||
4. **框架技术**:Spring Boot 3.5.7 + 若依框架
|
||
- 理由:项目现有框架;Spring Boot 3.5.7 支持 Java 21;若依框架提供完善的权限、缓存、日志等功能;社区活跃,文档完善
|
||
|
||
### 架构设计
|
||
1. **系统架构**:前端层(Vue 3)→ Controller 层 → Service 层 → Mapper 层 → 数据库层(MySQL 8.3.0)→ 缓存层(Redis)
|
||
2. **模块架构**:
|
||
- datai-admin(启动模块):Controller 层
|
||
- datai-system(系统模块):Service 层、Mapper 层、实体类
|
||
- datai-common(公共模块):工具类、切面、常量
|
||
- datai-plugins(插件模块):Redis 缓存工具类
|
||
|
||
### 数据流设计
|
||
1. **用户登录流程**:用户输入用户名密码 → Controller 接收登录请求 → Service 验证用户信息 → 从数据库读取用户货币偏好 → 从 Redis 缓存读取货币配置 → 将用户货币信息存储到 LoginUser → 生成 Token 并返回 → 前端存储 Token 和货币信息
|
||
2. **数据查询流程**:前端发起数据查询请求 → Controller 接收请求 → Service 层从数据库读取金额数据 → AOP 切面拦截响应 → 根据用户货币格式化金额 → 返回格式化后的金额 → 前端显示格式化后的金额
|
||
3. **汇率转换流程**:前端发起汇率转换请求 → Controller 接收请求 → Service 层从 Redis 缓存读取汇率 → 如果缓存未命中,从数据库读取汇率 → 返回转换后的金额 → 前端显示转换后的金额
|
||
4. **货币切换流程**:用户选择新货币 → Controller 接收货币切换请求 → Service 验证货币代码有效性 → 更新用户货币偏好到数据库 → 清除 Redis 缓存 → 刷新 Token → 前端刷新页面,重新加载金额数据
|
||
|
||
### 数据模型设计
|
||
1. **汇率表(sys_exchange_rate)**:
|
||
- 字段:id、from_currency、to_currency、rate、source、create_by、create_time、update_by、update_time、remark
|
||
- 索引:主键索引(id)、唯一索引(from_currency, to_currency)、普通索引(source)
|
||
2. **用户表修改(sys_user)**:
|
||
- 新增字段:currency_code(货币代码)
|
||
- 默认值:CNY(人民币)
|
||
- 位置:在 lang_code 字段之后
|
||
|
||
### 接口设计
|
||
1. **获取汇率列表**:GET /system/exchangeRate/list
|
||
- 权限要求:@PreAuthorize("@ss.hasPermi('system:exchangeRate:list')")
|
||
- 响应数据:汇率列表(包含 id、fromCurrency、toCurrency、rate、source)
|
||
2. **获取当前用户货币偏好**:GET /system/user/currency
|
||
- 权限要求:需要登录
|
||
- 响应数据:货币代码(如:CNY)
|
||
3. **切换货币偏好**:POST /system/user/switchCurrency
|
||
- 权限要求:需要登录
|
||
- 请求参数:currencyCode(货币代码)
|
||
- 响应数据:成功/失败消息
|
||
4. **汇率转换**:POST /system/exchangeRate/convert
|
||
- 权限要求:需要登录
|
||
- 请求参数:amount(金额)、fromCurrency(源货币)、toCurrency(目标货币)
|
||
- 响应数据:转换后的金额
|
||
5. **批量汇率转换**:POST /system/exchangeRate/batchConvert
|
||
- 权限要求:需要登录
|
||
- 请求参数:amounts(金额列表)、fromCurrency(源货币)、toCurrency(目标货币)
|
||
- 响应数据:转换后的金额列表
|
||
6. **更新汇率(手动)**:POST /system/exchangeRate/update
|
||
- 权限要求:@PreAuthorize("@ss.hasPermi('system:exchangeRate:edit')")
|
||
- 请求参数:fromCurrency(源货币)、toCurrency(目标货币)、rate(汇率)
|
||
- 响应数据:成功/失败消息
|
||
7. **更新汇率(API)**:POST /system/exchangeRate/updateFromApi
|
||
- 权限要求:@PreAuthorize("@ss.hasPermi('system:exchangeRate:edit')")
|
||
- 响应数据:成功更新的汇率数量
|
||
|
||
### 实现要点
|
||
1. **关键实现逻辑**:
|
||
- 货币格式化在 Service 层进行,使用 AOP 切面拦截响应
|
||
- 使用 CurrencyUtils 工具类进行货币格式化和转换
|
||
- 处理 null 值,避免空指针异常
|
||
- 处理无效货币代码,使用默认货币
|
||
- 支持多货币显示,自动转换并显示多种货币
|
||
2. **异常处理设计**:
|
||
- 货币格式化异常:捕获异常,记录日志,返回原始金额,提示用户货币格式化失败
|
||
- 货币代码无效异常:捕获异常,记录日志,返回错误响应,提示用户货币代码无效
|
||
- 缓存读取异常:捕获异常,记录日志,从数据库重新加载数据,提示用户缓存读取失败
|
||
- 汇率转换异常:捕获异常,记录日志,返回原始金额,提示用户汇率转换失败
|
||
3. **性能优化设计**:
|
||
- 缓存优化:使用 Redis 缓存汇率数据,汇率缓存 60 分钟,货币列表缓存 24 小时,默认货币缓存 24 小时
|
||
- 索引优化:为常用查询字段创建索引,提高查询性能
|
||
- 批量转换优化:批量转换时使用并行处理,合理设置线程池大小
|
||
4. **安全设计**:
|
||
- 数据验证:验证货币代码有效性、格式、白名单
|
||
- 权限控制:使用 @PreAuthorize 注解控制接口权限,使用数据权限控制数据访问范围
|
||
- 审计日志:使用 @Log 注解记录操作日志,记录操作人、操作时间、操作内容
|
||
|
||
## 相关文档
|
||
- [需求文档](../requirements/2026-01-21-002-05-货币格式化需求.md)
|
||
- [设计文档](../design/2026-01-21-002-05-货币格式化设计.md)
|
||
- [决策记录](../decisions/adr/2026-01-25-002-05-ADR-货币格式化技术选型.md)
|
||
- [SQL 脚本](../sql/2026-01-25-002-05-货币格式化.sql)
|
||
- [提示词文档](../prompts/2026-01-25-002-05-prompt-货币格式化功能.md)
|
||
- [代码文档](../reference-code/2026-01-25-002-05-code-货币格式化功能.md)
|
||
- [实施方案](../implementation/2026-01-25-002-05-implementation-货币格式化功能.md)
|