🟡 Analyzer 一般问题
以下问题影响系统可维护性、性能或用户体验,建议在后续迭代中修复。
🟡 P04 ✅ 已修复 transactions · CSV导入
category_id 未关联分类表,字段始终为 NULL
API:
GET /api/transactions/:billId说明:
transactions 表有 category_id 字段(外键 → categories.id),但 CSV 导入和直接插入时该字段从未被赋值,始终为 NULL。影响:
- 前端按分类筛选交易功能无法工作
- 分类数据存在但与 transactions 完全割裂
修复内容:
- ✅ CSV 导入时根据
category列的值去categories表匹配,填充category_id(通过 catMap 查找)
🟡 P05 ✅ 已修复 transactions · CSV导入
tx_year / tx_month 未生成,始终为 NULL
API:
GET /api/transactions/:billId说明:
transactions 表有 tx_year、tx_month 字段,但导入时从未计算赋值。影响:
- 按年月统计功能无法实现
- 月度收支报表无法直接用 SQL 查询
修复内容:
- ✅ CSV 导入时从
row['时间']解析年份和月份,存入tx_year和tx_month字段
🟡 P06
✅ 已修复 apikeys · 加密
api_key_encrypted 只用 Base64 编码,非真正加密
API:
PUT /api/apikeys/user · GET /api/apikeys/user修复内容:
- ✅ 使用
AES-256-GCM加密(server/utils.js的encryptText函数) - ✅ 存储格式:
AES:<base64(iv:authTag:ciphertext)> - ✅ 密钥从环境变量
API_KEY_ENCRYPTION_KEY(64位 hex)读取 - ✅ 解密时自动识别
AES:前缀(新格式)和旧 Base64 格式(兼容) - ✅ Admin 侧(A03)也使用相同加密体系
🟡 P07 ✅ 已过时 ai · 流式响应
Anthropic Provider 在 /chat 不走 SSE 流
API:
POST /api/ai/chat核实结论:
chatProviders.js 中不存在 anthropic / claude provider。当前支持的 Provider:OpenAI、Gemini、Zhipu、Minimax、DeepSeek、Moonshot、OpenRouter、SiliconFlow、xAI 等,均走 SSE 流。该问题描述的前提("Anthropic Key")在代码中不存在,文档已过时。修复状态:
- ✅ 问题描述已过时——代码中无 Anthropic Provider,无需修复
🟡 P08 ✅ 已修复 auth · 游客账号
游客账号限制无提示,用户困惑
API:
POST /api/auth/guest-login说明:同 IP 24h 内最多创建 2 个游客账号,超限后返回错误但无明确提示。
修复内容:
- ✅ 后端返回明确错误信息:
设备今日已达到试用上限。请注册正式账号或明天再试。 - ✅ 前端捕获并显示该错误消息
🟡 P09 auth · Token
Refresh Token 过期无恢复手段
API:
POST /api/auth/refresh说明:Refresh Token 过期后无法续期,必须重新登录。
影响:
- 用户 30 天不访问 = 强制重新登录
- 长期不活跃账号无法保留设置
改进建议:
- 延长 Refresh Token 有效期(如 365 天)
- 提供"记住我"选项
- 过期后通过邮箱验证码恢复会话
🟡 P10 auth · Session
多端 Session 无主动管理
API:
POST /api/auth/logout说明:当前
GET /api/auth/me 不返回在线设备列表,logout 接口只能按 refreshToken 删除单条。影响:
- 用户无法查看当前在哪些设备登录
- 无法远程注销其他设备
改进建议:
GET /api/auth/sessions— 返回所有在线 refreshToken(含设备/最后活跃时间)DELETE /api/auth/sessions/:tokenId— 主动注销指定设备DELETE /api/auth/sessions/all— 注销所有设备
🟡 P11 ✅ 已缓解 apikeys · 导入
跨账单导入 Key 时明文复制,无重加密
API:
POST /api/apikeys/import说明:导入时直接复制
api_key_encrypted 字段。修复状态:
- ✅ Admin 侧新 Key 已使用 AES-256-GCM 加密(A03 修复),导入时复制 AES 密文而非 Base64 明文
- ⚠️ 遗留:旧 Base64 编码 Key 导入时仍为明文复制(建议用户重新添加 Key 触发 AES 加密)
🟡 P12 ✅ 已修复 apikeys · 过期管理
Key 过期时间字段存在但无管理 UI
API:
api_keys.expires_at说明:
api_keys 表有 expires_at 字段,但前端 DeveloperPage 无过期管理功能,PATCH /apikeys/user 也不接受 expires_at 参数。修复内容:
- ✅ DeveloperPage (SettingsPage.jsx) 已实现过期时间选择器(date 输入框)
- ✅ Key 列表展示过期倒计时标签(
getExpirationStatus函数,颜色区分:7天内/已过期/正常) - ✅
handleUpdateDraft('expires_at')已绑定到编辑表单
✅ 已修复
🟡 P13 bills · AI提示词
custom_prompt 与 prompt 字段名不一致
API:
PUT /api/bills/:id/prompt说明:前端 DataContext.jsx 已修正,现发送
{ custom_prompt: "..." },与后端期望一致。AI Custom Prompt 设置功能已恢复正常。影响:
- 已修复 — AI Custom Prompt 设置功能正常工作
改进建议:
- 统一字段名,推荐以后端为准(
custom_prompt)修改前端代码
🟡 P14 ✅ 已修复 bills · 隐私
成员列表暴露其他成员手机号
API:
GET /api/bills/:id/members说明:
bill_members JOIN users 表时直接暴露了所有成员的 phone、nickname、avatar_url。同一账单的任意成员(包括 viewer)都可以看到其他成员的完整手机号。修复内容:
- ✅
bills.js后端使用maskPhone()过滤所有成员手机号:owner/本人显示完整号码,其他成员显示138 **** 1234格式 - ✅ 前端
BillPage.jsx额外使用maskPhoneDisplay()做展示层兜底
🟡 P15 ai · 语音识别
Whisper 仅支持 OpenAI,多 Provider 兜底失效
API:
POST /api/ai/transcribe说明:Whisper 语音转写强制依赖 OpenAI Key,没有其他 Provider 的 fallback。当 OpenAI Key 不可用或配额耗尽时,语音输入功能完全无法使用。
影响:
- 使用 Anthropic/Gemini 作为主 AI 的用户无法使用语音输入
- Whisper 没有类似 AI 对话的 Key 优先级兜底机制
改进建议:
- 支持第三方 ASR API(如 Google Speech-to-Text、Azure Speech)
- 或配置多个 OpenAI 兼容端点做兜底
🟡 P16 ✅ 已修复 ai · 错误处理
所有 Provider 失败时返回 502,无友好错误提示
API:
POST /api/ai/chat · POST /api/ai/voice-chat说明:当所有配置的 AI Provider 都返回错误时,后端直接返回 HTTP 502,错误信息为 Provider 的原始错误文本。
影响:
- 用户收到"502 Bad Gateway"而非友好的"AI 服务暂时不可用"
- 排查困难,不知道是 Key 过期、配额耗尽还是网络问题
修复内容:
- ✅
handleAiError()(server/ai/utils.js)统一处理所有异常,根据 status 分类返回友好消息 - ✅ 401 →
503 + "AI 服务身份验证失败,请检查 API Key 配置或有效期" - ✅ 429 →
503 + "AI 服务额度耗尽或每分钟调用次数受限,请稍后再试" - ✅ 408/504/超时 →
502 + "AI 服务器响应超时,请检查网络连接或稍后重试" - ✅ 5xx →
502 + "AI 接口提供商服务器内部错误,请稍后重试" - ✅ 返回结构:
{ error, code, details, provider },details 保留原始错误供调试
🟡 P17 ✅ 已修复 ai · 日志记录
AI 日志记录在 Provider 层,异常时不记录
API:
POST /api/ai/chat(通过 proxyChatByProvider)说明:AI 调用日志(
api_logs 表)在 proxyChatByProvider 成功返回后写入。如果 Provider 直接抛出异常(如网络超时、Key 无效),日志记录语句不会执行。影响:
- 失败的 AI 请求不在
api_logs中留下痕迹 - Admin 后台的 AI 日志查询只能看到成功记录,排查问题不完整
改进建议:
- 在 try/catch 外层也记录失败日志(记录 error message、provider、model)
- 或使用
finally确保日志写入
🟡 P18 ✅ 已修复 transactions · CSV导入
coupon 字段类型为 TEXT 但存数字字符串
API:
POST /api/transactions/:billId/upload说明:
coupon(优惠券)字段在数据库是 TEXT 类型,但导入代码用 parseFloat() 转成数字再存入。parseFloat("无") = NaN || 0 = 0,实际含义丢失。修复内容:
- ✅
coupon直接存储为字符串,不做 parseFloat 转换,保留原始值(如"无"、折扣说明等)
🟡 P19 ✅ 已修复 transactions · CSV导入
CSV 部分列读取后从未入库
API:
POST /api/transactions/:billId/upload说明:CSV 导入时读取了以下列的值,但从未 INSERT 到
transactions 表:已报销(reimbursed)、记账者(bookkeeper)、关联单笔(related_tx)。修复内容:
- ✅ 新增字段入库:
reimbursed(来源:是否报销/报销状态)、coupon(来源:优惠券/优惠)、bookkeeper(来源:记账人)、related_tx(来源:关联单号/关联交易)
影响:
- 这些列的数据被白白丢弃,用户上传的对应信息完全没意义
- 如果未来需要这些功能,需要修改数据库和导入逻辑
改进建议:
- 在
transactions表增加reimbursed、bookkeeper、related_tx字段 - 或在校验时提示用户这些字段暂不支持