fundplatform/doc/开发问题清单.md

756 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 资金服务平台 - 开发问题清单
> **文档版本**: v1.0
> **创建日期**: 2026-02-13
> **记录时段**: 2026-02-12 09:00 ~ 2026-02-13 当前时间
---
## 问题清单概览
| 序号 | 问题分类 | 问题描述 | 严重程度 | 状态 |
|------|----------|----------|----------|------|
| 1 | 前后端接口 | /auth/info接口返回400 Bad Request | 高 | 已解决 |
| 2 | 前端路由 | 个人中心和系统设置菜单点击无反应 | 中 | 已解决 |
| 3 | 前端路由 | 刷新页面显示404 | 高 | 已解决 |
| 4 | 后端序列化 | AOP日志LocalDateTime序列化失败 | 中 | 已解决 |
| 5 | 后端接口 | 新增支出expenseType字段雪花ID超出Integer范围 | 高 | 已解决 |
| 6 | OpenFeign配置 | FeignChainInterceptor未注册为Spring Bean | 高 | 已解决 |
| 7 | 全链路追踪 | TraceContextHolder未在HTTP入口初始化导致链路断裂 | 高 | 已解决 |
| 8 | OpenFeign配置 | FeignClient硬编码URL导致Nacos服务发现失效 | 高 | 已解决 |
| 9 | Nacos 配置 | Nacos 3.0 客户端缺少 username/password 认证配置 | 高 | 已解决 |
| 10 | 连接池监控 | HikariMonitorConfig空指针异常 | 中 | 已解决 |
---
## 问题详情与解决方案
### 问题1/auth/info接口返回400 Bad Request
#### 问题现象
```
Request URL: http://localhost:3002/sys/api/v1/auth/info
Request Method: GET
Status Code: 400 Bad Request
```
用户登录成功后,调用 `/auth/info` 获取用户信息时返回400错误。
#### 问题原因
后端接口需要 `X-User-Id` 请求头来识别当前用户但前端未在请求中传递该header。
#### 解决方案
**1. 修改前端请求拦截器** (`fund-admin/src/api/request.ts`)
```typescript
// 在请求头中添加 X-User-Id
request.headers['X-User-Id'] = localStorage.getItem('userId') || ''
```
**2. 修改用户登录逻辑** (`fund-admin/src/stores/user.ts`)
```typescript
// 登录成功后保存 userId 和 tenantId 到 localStorage
localStorage.setItem('userId', String(res.data.userId))
localStorage.setItem('tenantId', String(res.data.tenantId))
```
#### 经验总结
- 前后端接口对接时,需确认所有必需的请求头
- 用户信息userId、tenantId应在登录成功后立即持久化存储
---
### 问题2个人中心和系统设置菜单点击无反应
#### 问题现象
点击顶部下拉菜单中的"个人中心"和"系统设置"选项后,页面无任何跳转反应。
#### 问题原因
`MainLayout.vue` 中的 `handleCommand` 函数对 `profile``settings` 两个命令的处理为空实现。
```typescript
// 问题代码
const handleCommand = async (command: string) => {
switch (command) {
case 'profile':
// 空实现,无任何代码
break
case 'settings':
// 空实现,无任何代码
break
case 'logout':
await userStore.logout()
router.push('/login')
break
}
}
```
#### 解决方案
**修改 `fund-admin/src/layouts/MainLayout.vue`**
```typescript
const handleCommand = async (command: string) => {
switch (command) {
case 'profile':
router.push('/profile')
break
case 'settings':
router.push('/system/config')
break
case 'logout':
await userStore.logout()
router.push('/login')
break
}
}
```
同时在侧边栏菜单中添加"参数设置"入口。
#### 经验总结
- 开发新功能时需确保所有UI交互都有对应的业务实现
- 代码提交前应进行基础功能测试
---
### 问题3刷新页面显示404
#### 问题现象
登录后进入业务页面刷新浏览器后页面显示404
```
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
type=Not Found, status=404
```
#### 问题原因
Vite 开发服务器的代理配置中,`/sys` 路径模糊匹配了前端路由 `/system`,导致前端路由请求被错误代理到后端服务。
```typescript
// 问题配置
proxy: {
'/sys': { // 会匹配 /sys 开头的所有路径,包括 /system
target: 'http://localhost:8100',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/sys/, '')
}
}
```
#### 解决方案
**修改 `fund-admin/vite.config.ts`**
```typescript
proxy: {
'/sys/': { // 添加斜杠,精确匹配 /sys/ 开头的路径
target: 'http://localhost:8100',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/sys/, '')
}
}
```
同时改进路由守卫逻辑,更好地处理页面刷新场景。
#### 经验总结
- 代理路径配置应使用精确匹配,避免模糊匹配导致的路由冲突
- 前端路由命名应与后端API路径有明显区分
---
### 问题4AOP日志LocalDateTime序列化失败
#### 问题现象
为 fund-sys 添加 AOP 日志功能后,日志输出时报错:
```
Java 8 date/time type 'java.time.LocalDateTime' not supported by default:
add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
to enable handling
```
#### 问题原因
Jackson 默认不支持 Java 8 的日期时间类型LocalDateTime、LocalDate等的序列化需要额外配置。
#### 解决方案
**1. 添加 Maven 依赖** (`fund-sys/pom.xml`)
```xml
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
```
**2. 配置 ObjectMapper** (`ApiLogAspect.java`)
```java
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
// 注册JavaTimeModule以支持Java 8日期时间类型
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
```
**3. 序列化异常兜底处理**
```java
try {
logInfo.put("responseBody", objectMapper.writeValueAsString(result));
} catch (Exception e) {
// 序列化失败时使用toString()兜底
logInfo.put("responseBody", result.toString());
}
```
#### 经验总结
- 使用 Jackson 序列化 Java 8 日期时间类型时,必须注册 `JavaTimeModule`
- 对于复杂的序列化场景,应添加异常兜底处理,避免日志功能影响业务
- Spring Boot 项目使用 AOP 需引入 `spring-boot-starter-aop` 依赖
---
### 问题5新增支出expenseType字段雪花ID超出Integer范围
#### 问题现象
```
POST http://localhost:8080/exp/api/v1/exp/expense
Status Code: 400 Bad Request
```
移动端新增支出时,请求返回 400 错误,无任何响应内容。
#### 问题原因
后端 `FundExpenseDTO.expenseType` 字段类型为 `Integer`,最大值为 2147483647。而前端传递的是雪花ID`2023686600025919489`),远超 Integer 范围,导致 JSON 解析失败。
#### 解决方案
**修改 `fund-exp/src/main/java/com/fundplatform/exp/dto/FundExpenseDTO.java`**
```java
// 修改前
@NotNull(message = "支出类型不能为空")
private Integer expenseType;
public Integer getExpenseType() { return expenseType; }
public void setExpenseType(Integer expenseType) { this.expenseType = expenseType; }
// 修改后
@NotNull(message = "支出类型不能为空")
private Long expenseType;
public Long getExpenseType() { return expenseType; }
public void setExpenseType(Long expenseType) { this.expenseType = expenseType; }
```
#### 经验总结
- 使用雪花ID作为主键的实体其外键字段类型必须使用 `Long` 而非 `Integer`
- Java Integer 范围:-2147483648 ~ 2147483647约21亿
- 雪花ID范围通常为19位数字远超 Integer 范围
---
### 问题6FeignChainInterceptor未注册为Spring Bean
#### 问题现象
OpenFeign 调用下游服务时租户ID、用户信息、TraceId 等上下文信息未透传,导致下游服务无法获取当前请求的租户和用户信息。
#### 问题原因
`FeignChainInterceptor` 虽然实现了 `RequestInterceptor` 接口,但缺少 `@Component` 注解,未被 Spring 注册为 Bean导致 OpenFeign 调用时拦截器不生效。
```java
// 问题代码
public class FeignChainInterceptor implements RequestInterceptor {
// 没有 @Component 注解
}
```
#### 解决方案
**1. 添加 Bean 注解** (`fund-common/src/main/java/com/fundplatform/common/feign/FeignChainInterceptor.java`)
```java
@Component // 添加此注解
public class FeignChainInterceptor implements RequestInterceptor {
// ...
}
```
**2. 配置组件扫描** (`fund-report/src/main/java/com/fundplatform/report/ReportApplication.java`)
```java
@SpringBootApplication(scanBasePackages = {"com.fundplatform.report", "com.fundplatform.common"})
@EnableDiscoveryClient
@EnableFeignClients
public class ReportApplication {
// ...
}
```
#### 经验总结
- Spring 组件Service、Component、Repository 等)必须添加相应注解才能被容器管理
- 跨模块的组件需确保使用该组件的模块配置了正确的 `scanBasePackages`
- Feign 拦截器是实现微服务间上下文透传的关键,必须确保正确注册
---
### 问题 7TraceContextHolder 未在 HTTP 入口初始化导致链路断裂
#### 问题现象
Gateway 层已生成 TraceId 并写入请求头 `X-Trace-Id`,但业务服务中调用 `TraceContextHolder.getTraceId()` 返回 nullFeign 调用时会重新生成新的 TraceId导致全链路追踪断裂。
#### 问题原因
`ContextInterceptor` 只提取了租户 ID 和用户 ID未从请求头中提取 Gateway 传递的 TraceId 并设置到 `TraceContextHolder`,导致:
1. 业务逻辑无法获取 Gateway 生成的 TraceId
2. Feign 调用时通过 `getOrCreateTraceId()` 生成新的 TraceId
3. 日志系统中出现多个不同的 TraceId无法串联完整调用链
```java
// 问题代码 - ContextInterceptor.preHandle
// 缺少 TraceId 的提取逻辑
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 只设置了 TenantId 和 UserId
TenantContextHolder.setTenantId(tenantId);
UserContextHolder.setUserId(userId);
// 缺失TraceContextHolder.setTraceId(traceId);
return true;
}
```
#### 解决方案
**修改 `fund-common/src/main/java/com/fundplatform/common/web/ContextInterceptor.java`**
1. 添加导入和常量定义
```java
import com.fundplatform.common.context.TraceContextHolder;
public static final String HEADER_TRACE_ID = "X-Trace-Id";
```
2. 在 preHandle 中提取 TraceId
```java
// 提取 TraceId如不存在后续会在需要时自动生成
String traceId = request.getHeader(HEADER_TRACE_ID);
if (traceId != null && !traceId.isEmpty()) {
TraceContextHolder.setTraceId(traceId);
}
```
3. 在 afterCompletion 中清理 TraceContextHolder
```java
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
TenantContextHolder.clear();
UserContextHolder.clear();
TraceContextHolder.clear(); // 新增
}
```
#### 修复后的完整链路
```
Gateway (GlobalLogFilter)
↓ 生成 TraceId: abc123 并写入 X-Trace-Id 请求头
业务服务 A (ContextInterceptor.preHandle)
↓ 读取 X-Trace-Id: abc123 并设置到 TraceContextHolder
业务逻辑 A
↓ 使用 TraceContextHolder.getTraceId() → abc123
FeignChainInterceptor.apply
↓ 从 TraceContextHolder 获取 abc123 并透传到下游
业务服务 B (ContextInterceptor.preHandle)
↓ 读取 X-Trace-Id: abc123 并设置到 TraceContextHolder
业务逻辑 B
↓ 使用 TraceContextHolder.getTraceId() → abc123
afterCompletion
↓ 清理所有 ThreadLocal
```
#### 经验总结
- Gateway 生成的 TraceId 必须在业务服务入口处提取并设置到 ThreadLocal
- Feign 拦截器应从 ThreadLocal 获取 TraceId 并透传,而不是重新生成
- 所有 ThreadLocal 使用后必须在 finally 或 afterCompletion 中清理,防止内存泄漏
- 全链路追踪需要每个环节都正确传递 TraceId任何一环断裂都会导致追踪失败
---
### 问题 8FeignClient 硬编码 URL 导致 Nacos 服务发现失效
#### 问题现象
已配置 Nacos 作为服务注册中心,但各 FeignClient 仍使用 `url = "${feign.xxx.url:http://localhost:xxxx}"` 硬编码地址,导致:
1. Nacos 服务注册中心形同虚设
2. Feign 不会通过 Nacos 进行服务发现和负载均衡
3. 无法实现服务动态扩缩容和高可用
```java
// 问题代码 - ExpenseFeignClient.java
@FeignClient(name = "fund-exp", url = "${feign.fund-exp.url:http://localhost:8140}")
public interface ExpenseFeignClient {
// ...
}
```
#### 问题原因
**架构设计不一致**
- ✅ 已在 `application.yml` 中配置 Nacos 服务发现
- ❌ 但 FeignClient 仍指定了固定的 URL 地址
**可能的历史原因**
1. **开发环境过渡方案**:本地开发时 Nacos 未部署,临时使用直连方式
2. **迁移遗留问题**:从单体架构迁移到微服务时的过渡配置
3. **理解偏差**:误以为 FeignClient 必须指定 url
#### 解决方案
**修复前(❌)**
```java
@FeignClient(name = "fund-exp", url = "${feign.fund-exp.url:http://localhost:8140}")
// ↓ 直接访问固定地址Nacos 不生效
```
**修复后(✅)**
```java
@FeignClient(name = "fund-exp")
// ↓ 通过 Nacos 服务发现 + Ribbon 负载均衡
```
**涉及的文件修改**
1. `ExpenseFeignClient.java` - 移除 `url` 属性
2. `ReceivableFeignClient.java` - 移除 `url` 属性
3. `ProjectFeignClient.java` - 移除 `url` 属性
4. `SysServiceClient.java` - 移除 `url` 属性
#### 修复后的架构优势
**工作流程:**
```
Fund-Report (调用方)
↓ FeignClient: fund-exp
↓ Spring Cloud OpenFeign + Nacos Discovery
↓ 从 Nacos 查询 fund-exp 服务的所有实例
↓ Ribbon/LoadBalancer 进行负载均衡选择实例
↓ 发起 HTTP 请求到选中的实例
```
**带来的好处:**
| 特性 | 修复前 | 修复后 |
|------|--------|--------|
| **服务发现** | ❌ 固定地址 | ✅ 自动感知上下线 |
| **负载均衡** | ❌ 单点 | ✅ 多实例轮询 |
| **高可用** | ❌ 故障无法切换 | ✅ 自动切换健康实例 |
| **弹性扩缩容** | ❌ 需修改配置 | ✅ 新增实例无需配置 |
| **架构一致性** | ❌ Nacos 形同虚设 | ✅ 充分发挥作用 |
#### 正确的 FeignClient 配置规范
| 场景 | 配置方式 | 示例 |
|------|---------|------|
| **有注册中心(推荐)** | 只指定 name | `@FeignClient(name = "fund-exp")` |
| **无注册中心** | 指定 name + url | `@FeignClient(name = "xxx", url = "http://localhost:8080")` |
| **需要降级** | 指定 name + fallback | `@FeignClient(name = "fund-exp", fallback = ExpFallback.class)` |
#### 必备配置要求
**1. application.yml 配置**
```yaml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos 地址
namespace: fund-platform # 命名空间
group: DEFAULT_GROUP # 分组
```
**2. 启动类注解**
```java
@SpringBootApplication
@EnableDiscoveryClient // 启用服务发现
@EnableFeignClients // 启用 Feign 客户端
public class ReportApplication {
// ...
}
```
#### 经验总结
- **有注册中心时必须移除 url 属性**,让 Feign 通过注册中心发现服务
- **url 属性仅用于开发测试或特殊场景**(如直连第三方服务)
- **Nacos 的价值在于服务发现与负载均衡**,硬编码 URL 会让这些能力失效
- **微服务架构应充分利用注册中心**,避免回到点对点调用的老路
---
### 问题 9Nacos 3.0 客户端缺少 username/password 认证配置
#### 问题现象
Nacos 3.0 版本升级后,各业务服务启动时无法注册到 Nacos控制台报错
```
com.alibaba.nacos.api.exception.NacosException:
Error Code: 403, Error Msg: authorization failed
```
或日志中出现:
```
WARN com.alibaba.nacos.client.naming - [NA] failed to call server
com.alibaba.nacos.api.exception.NacosException:
Client need token, but token is empty.
```
#### 问题原因
**Nacos 3.0 安全策略变更**
- Nacos 2.x 版本:客户端可以匿名访问服务端
- Nacos 3.0 版本:**强制要求客户端提供用户名和密码进行认证**
**默认凭证**
- 用户名:`nacos`
- 密码:`nacos`
**影响范围**
所有配置了 Nacos 服务发现的模块都需要添加认证信息。
#### 解决方案
**修复前(❌)**
```yaml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: fund-platform
group: DEFAULT_GROUP
# 缺少 username 和 password
```
**修复后(✅)**
```yaml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: fund-platform
group: DEFAULT_GROUP
username: nacos # Nacos 3.0 必需
password: nacos # Nacos 3.0 必需
```
**涉及的所有模块**
| 模块 | 服务名 | 配置文件位置 |
|------|--------|-------------|
| fund-sys | fund-sys | `fund-sys/src/main/resources/application.yml` |
| fund-cust | fund-cust | `fund-cust/src/main/resources/application.yml` |
| fund-exp | fund-exp | `fund-exp/src/main/resources/application.yml` |
| fund-proj | fund-proj | `fund-proj/src/main/resources/application.yml` |
| fund-req | fund-req | `fund-req/src/main/resources/application.yml` |
| fund-receipt | fund-receipt | `fund-receipt/src/main/resources/application.yml` |
| fund-file | fund-file | `fund-file/src/main/resources/application.yml` |
| fund-report | fund-report | `fund-report/src/main/resources/application.yml` |
#### 验证方法
**1. 查看服务注册状态**
访问 Nacos 控制台http://localhost:8048/
- 登录账号:`nacos` / `nacos`
- 进入「服务管理」→「服务列表」
- 应该能看到所有服务已成功注册
**2. 检查服务日志**
启动日志中应该出现:
```
INFO c.a.cloud.nacos.registry.NacosServiceRegistry -
nacos registry, DEFAULT_GROUP fund-sys 10.244.21.185:8100 register finished
```
**3. 测试服务调用**
```bash
curl http://localhost:8000/sys/api/v1/sys/health
# 应该能正常返回健康检查结果
```
#### Nacos 3.0 vs 2.x 主要变化
| 特性 | Nacos 2.x | Nacos 3.0 | 影响 |
|------|-----------|-----------|------|
| **客户端认证** | ❌ 不需要 | ✅ 强制要求 | 必须配置 username/password |
| **Token 机制** | ❌ 无 | ✅ JWT Token | 客户端需携带有效 Token |
| **权限控制** | ❌ 弱 | ✅ 强化 | 支持细粒度 RBAC 权限管理 |
| **审计日志** | ❌ 基础 | ✅ 完整 | 记录所有敏感操作 |
#### 经验总结
- **Nacos 3.0 升级后必须修改客户端配置**,否则无法正常注册
- **默认用户名和密码都是 `nacos`**,生产环境建议修改为强密码
- **Gateway 不需要配置 discovery**,但需要通过 `@EnableDiscoveryClient` 启用服务发现
- **认证失败会报 403 错误**,而不是连接失败的错误
---
### 问题 10HikariMonitorConfig 空指针异常
#### 问题现象
服务启动后,定时任务日志中持续报错:
```
java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null
at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38)
```
#### 问题原因
**HikariCP 连接池初始化时机问题**
- `HikariDataSource.getHikariPoolMXBean()` 方法在连接池尚未初始化时返回 `null`
- 定时任务在应用启动后立即执行,此时连接池可能还未完成初始化
- 直接调用 `getActiveConnections()` 等方法会导致空指针异常
**相关代码**
```java
// 问题代码
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.MINUTES)
public void monitorHikariPool() {
if (dataSource instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
// 未检查 getHikariPoolMXBean() 返回值是否为 null
log.info("活跃连接数: {}", hikariDataSource.getHikariPoolMXBean().getActiveConnections());
}
}
```
#### 解决方案
**修复后代码**
```java
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.MINUTES)
public void monitorHikariPool() {
if (dataSource instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
HikariConfigMXBean config = hikariDataSource.getHikariConfigMXBean();
var poolMXBean = hikariDataSource.getHikariPoolMXBean();
// 检查连接池是否已初始化
if (poolMXBean == null) {
log.debug("HikariCP 连接池尚未初始化,跳过监控");
return;
}
log.info("=== HikariCP 连接池状态 ===");
log.info("连接池名称: {}", config.getPoolName());
log.info("活跃连接数: {}", poolMXBean.getActiveConnections());
log.info("空闲连接数: {}", poolMXBean.getIdleConnections());
log.info("等待获取连接的线程数: {}", poolMXBean.getThreadsAwaitingConnection());
log.info("最大连接数: {}", config.getMaximumPoolSize());
log.info("最小空闲连接数: {}", config.getMinimumIdle());
}
}
```
**修复要点**
1. 先获取 `HikariPoolMXBean` 实例并存储到变量
2. 检查是否为 `null`,为 `null` 则跳过本次监控
3. 使用变量访问连接池信息,避免重复调用 `getHikariPoolMXBean()`
#### 修复文件
- `fund-sys/src/main/java/com/fundplatform/sys/config/HikariMonitorConfig.java`
#### 经验总结
- **调用可能返回 null 的方法前必须做空值检查**
- **定时任务执行时需考虑资源初始化时机**,不能假设资源已就绪
- **使用变量存储对象引用**,避免重复调用 getter 方法
---
## 预防措施清单
### 前后端接口对接
| 检查项 | 说明 |
|--------|------|
| 请求头确认 | 确认所有必需的请求头X-User-Id、X-Tenant-Id、Authorization等 |
| 响应格式确认 | 统一使用 `Result<T>` 包装响应,确认字段命名一致 |
| 错误码定义 | 明确各类错误码的含义和处理方式 |
### 前端路由配置
| 检查项 | 说明 |
|--------|------|
| 代理路径精确匹配 | 使用 `/api/` 带斜杠的形式,避免模糊匹配 |
| 路由命名规范 | 前端路由与后端API路径应使用不同的前缀区分 |
| 刷新兼容性 | SPA 应用需考虑页面刷新时的路由状态恢复 |
### 后端开发规范
| 检查项 | 说明 |
|--------|------|
| AOP 依赖引入 | 使用 AOP 需添加 `spring-boot-starter-aop` 依赖 |
| Jackson 日期配置 | 序列化 Java 8 日期类型需注册 `JavaTimeModule` |
| 日志独立输出 | 使用 Logger name 隔离,配置独立的 appender |
| 外键字段类型 | 使用雪花 ID 的外键字段必须使用 Long 类型 |
| Feign 拦截器注册 | Feign 拦截器必须添加@Component 注解并配置组件扫描 |
| TraceId 传递 | ContextInterceptor 必须提取并设置 TraceId |
| **FeignClient 配置** | **有注册中心时必须移除 url 属性,仅保留 name** |
| **Nacos 3.0 认证** | **客户端必须配置 username 和 password默认都是 nacos** |
| **定时任务资源检查** | **定时任务访问资源前需检查资源是否已初始化(如连接池)** |
### 功能开发流程
| 检查项 | 说明 |
|--------|------|
| UI交互完整性 | 所有菜单、按钮的点击事件都应有对应实现 |
| 自测覆盖 | 代码提交前进行基础功能自测 |
| 刷新测试 | 重点测试页面刷新后的状态是否正常 |
---
## 相关文件索引
### 本次问题修复涉及的文件
**后端文件:**
- `fund-sys/pom.xml` - 添加 AOP 和 Jackson 依赖
- `fund-sys/src/main/java/com/fundplatform/sys/aop/ApiLogAspect.java` - AOP 日志切面
- `fund-sys/src/main/resources/logback-spring.xml` - 日志配置
- `fund-exp/src/main/java/com/fundplatform/exp/dto/FundExpenseDTO.java` - 支出 DTO expenseType 字段类型修复
- `fund-common/src/main/java/com/fundplatform/common/feign/FeignChainInterceptor.java` - Feign 拦截器添加@Component 注解
- `fund-report/src/main/java/com/fundplatform/report/ReportApplication.java` - 配置组件扫描路径
- `fund-common/src/main/java/com/fundplatform/common/web/ContextInterceptor.java` - 添加 TraceId 提取与清理逻辑
- `fund-report/src/main/java/com/fundplatform/report/feign/ExpenseFeignClient.java` - 移除硬编码 URL使用 Nacos 服务发现
- `fund-report/src/main/java/com/fundplatform/report/feign/ReceivableFeignClient.java` - 移除硬编码 URL使用 Nacos 服务发现
- `fund-report/src/main/java/com/fundplatform/report/feign/ProjectFeignClient.java` - 移除硬编码 URL使用 Nacos 服务发现
- `fund-cust/src/main/java/com/fundplatform/cust/feign/SysServiceClient.java` - 移除硬编码 URL使用 Nacos 服务发现
- `fund-*/src/main/resources/application.yml` - 为所有业务模块添加 Nacos 3.0 username/password 认证配置
**前端文件:**
- `fund-admin/vite.config.ts` - 代理配置修复
- `fund-admin/src/router/index.ts` - 路由配置
- `fund-admin/src/layouts/MainLayout.vue` - 菜单事件处理
- `fund-admin/src/api/request.ts` - 请求拦截器
- `fund-admin/src/stores/user.ts` - 用户状态管理
---
## 附录:常见错误速查
| 错误信息 | 可能原因 | 解决方案 |
|----------|----------|----------|
| `400 Bad Request` | 缺少必需请求头 | 检查X-User-Id、X-Tenant-Id等 |
| `404 Not Found (刷新后)` | 代理路径冲突 | 使用精确匹配,区分前后端路由 |
| `Java 8 date/time type not supported` | Jackson未配置日期模块 | 注册JavaTimeModule |
| `ClassNotFoundException: Aspect` | 未引入AOP依赖 | 添加spring-boot-starter-aop |
| `JSON parse error` | 字段类型不匹配Integer vs Long | 雪花ID外键必须使用Long类型 |
| `Feign 调用未透传上下文` | 拦截器未注册为 Bean | 添加@Component 注解并配置扫描 |
| `TraceId 链路断裂` | ContextInterceptor 未提取 TraceId | 在 preHandle 中添加 TraceId 设置逻辑 |
| `服务发现失效` | FeignClient 硬编码 URL | 移除 url 属性,仅保留 name 使用 Nacos 发现 |
| `Nacos 注册失败 (认证错误)` | 缺少 username/password 配置 | 在 discovery 节点添加 username 和 password |
| `HikariPoolMXBean NullPointerException` | 定时任务执行时连接池未初始化 | 添加 poolMXBean 空值检查 |
---
> **备注**: 本文档将根据后续开发过程中的问题持续更新。