hailin
a5cc3fdc5b
fix(mobile-app): 修复切换账号时 token 过期导致自动退出登录
...
根因:switchToAccount() 流程中,定时器在 _clearCurrentAccountData()
之后才停止。clear 阶段会删除 token,但此时定时器仍在运行,
in-flight 的 API 请求收到 401 → 触发 _handleTokenExpired()
→ 调用 logoutCurrentAccount() 把正在恢复的新账号数据全部清掉
→ 用户被自动踢到登录页面。
修复:将 onBeforeRestore 回调(停止定时器)移到 _clearCurrentAccountData()
之前执行,确保所有 API 请求停止后再清除 token。
修改前: save → clear(删token) → 停定时器 → restore
修改后: save → 停定时器 → clear(删token) → restore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:21:57 -08:00
hailin
cfc03fe523
fix(pricing): 手动调价支持负数降价对冲
...
之前手动调价只允许非负整数,无法用负数对冲降价。
前端 (admin-web settings/page.tsx):
- 移除 input min="0" 限制,允许输入负数
- 验证改为:只校验 isNaN 和总价不低于 0(15831 + amount >= 0)
- 文案:"加价金额" → "调价金额",placeholder 改为"正数涨价,负数降价"
- 实时预览条件从 amount >= 0 改为总价 >= 0
- 提示文案更新为"正数涨价,负数降价对冲"
后端 (admin-service tree-pricing.service.ts):
- 移除 newSupplement < 0 的硬性拒绝
- 改为校验 BASE_PRICE(15831) + newSupplement >= 0,防止总价为负
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:58:11 -08:00
hailin
12004d1c2e
fix(mobile-app): 修复账号切换后自动退出登录的问题
...
根因:ref.invalidate(authProvider) 销毁旧 AuthNotifier 后,新实例的构造函数
仅设置 AuthState(status: AuthStatus.initial),从不自动调用 checkAuthStatus()
从 SecureStorage 重新加载认证数据。导致 auth 状态停留在 initial(未认证),
依赖 auth 状态的组件误判为"未登录",触发页面跳转到登录页。
修复:
- account_switch_page: invalidate 后立即调用 loadAuthState() 从 storage
读取新账号数据,确保 auth 状态为 authenticated 后再导航
- account_switch_page: 切换后重置 ApiClient 的 tokenExpired 标记,防止
旧会话的 401 状态阻塞新账号的请求
- app.dart: _handleTokenExpired() 增加醒目日志和调用栈打印,便于排查
切换期间是否有 token 过期事件被误触发
切换流程更新为 6 步:
[1/6] switchToAccount() - 保存旧账号、清空、恢复新账号 storage
[2/6] onBeforeRestore - 停止所有定时器
[3/6] invalidate Provider - 销毁旧 Provider 实例
[4/6] loadAuthState() - 从 storage 加载新账号 auth 状态 ← 新增关键步骤
[5/6] 恢复遥测上传
[6/6] 导航到 ranking 页面
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:39:31 -08:00
hailin
83ba9b7d54
fix(admin-service): 定价DTO添加class-validator装饰器,修复400错误
...
与auth-service支付密码DTO同样的问题:ValidationPipe的
forbidNonWhitelisted:true 导致无装饰器的DTO属性被拒绝。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:50:45 -08:00
hailin
81ea35b712
fix(admin-service): 修复预种价格计算公式,3566不是15831/5
...
totalPortionPrice 之前用 Math.floor(totalPrice/5) = 3166,但预种
价格 3566 是各权益项 floor(amount/5) 之和 + 总部吸收余额,不是
简单的整棵价格除以5。
修正为: BASE_PORTION_PRICE(3566) + floor(currentSupplement/5)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:48:07 -08:00
hailin
3a307b5db7
fix(planting): 修复认种页面动态定价不生效 + 添加涨价倒计时
...
- 修复 admin-service PublicTreePricingController 路由双重前缀问题
(@Controller('api/v1/tree-pricing') → @Controller('tree-pricing'))
- Kong 网关新增 /api/v1/tree-pricing 路由到 admin-service
- mobile-app 认种页面添加涨价倒计时功能:
显示"距下次涨价还有 X天 X小时 X分钟"及涨价后价格
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:22:27 -08:00
hailin
19f350b8e3
fix(mining-app): 全网兑换销毁量改为保留8位小数
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:54:53 -08:00
hailin
c293309bdf
fix(mining-app): 全网兑换销毁量保留4位小数
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:53:47 -08:00
hailin
a39af93063
fix(mining-app): 全网兑换销毁量改用circulationPool+保留小数格式
...
根据业务需求,"全网兑换销毁量"应显示流通池总数量(全网卖出量),
而非SELL_BURN销毁总量。同时将formatIntWithCommas改为formatWithCommas
以保留小数位,避免小数值被截断显示为0。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:53:14 -08:00
hailin
edc81cc55d
fix(trading+mining-app): 修复"全网兑换销毁量"显示为0的问题
...
根因:前端"全网兑换销毁量"读取的是circulationPool(流通池积分股),
但实际应该显示burn_records中source_type=SELL_BURN的销毁总量。
这是两个不同的概念:circulationPool是卖出交易进入流通的积分股,
而兑换销毁量是卖出时被销毁(进入黑洞)的积分股。
修复:
- 后端: BlackHoleRepository添加getTotalSellBurned()聚合查询
- 后端: asset.service.ts市场概览API新增totalSellBurned字段
- 前端: MarketOverview实体/Model新增totalSellBurned字段
- 前端: trading_page销毁明细弹窗改用totalSellBurned显示
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:52:10 -08:00
hailin
74f061cfeb
fix(auth-service): 修复支付密码DTO缺少class-validator装饰器导致请求被ValidationPipe拒绝
...
根因:ValidationPipe配置了whitelist+forbidNonWhitelisted,但DTO类的属性
没有任何class-validator装饰器,导致所有请求体属性被当作非白名单属性直接
返回Bad Request,请求根本未到达业务逻辑层。
修复:为SetTradePasswordDto、ChangeTradePasswordDto、VerifyTradePasswordDto
添加@IsString()和@IsNotEmpty()装饰器。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:39:14 -08:00
hailin
16da1d20f0
fix(auth): 修复设置支付密码时报错的问题
...
支付密码是6位纯数字,但 setTradePassword 调用了 Password.create()
走了登录密码的格式验证(要求≥8位+字母+数字),导致必然抛出异常。
新增 Password.createWithoutValidation() 方法,仅做 bcrypt hash
不走格式验证。支付密码的格式验证由 trade-password.service.ts 独立处理。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:01:33 -08:00
hailin
4a1bf3aafe
fix(trading): 修复已分配积分股显示为0的问题
...
mining-service 返回格式为 { success: true, data: { totalDistributed: "..." } }
但 getTotalMinedFromMiningService() 直接取 result.totalDistributed,
应该取 result.data.totalDistributed。
同时兼容两种格式,优先取 result.data.totalDistributed。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:36:38 -08:00
hailin
acf55b26a7
feat(pricing): 预种每份价格从 3171 调整为 3566 绿积分
...
分配规则:按 reward-service RIGHT_AMOUNTS(15831 整棵树)各项 /5 取整,
余额全归总部社区(HQ_BASE_FEE)。5 份合成一棵树 = 17830。
10 类分配金额变更:
- COST_FEE: 576 (不变, floor(2880/5))
- OPERATION_FEE: 420 (不变, floor(2100/5))
- HQ_BASE_FEE: 29.4 → 427 (3566 - 3139, 吸收全部余额)
- RWAD_POOL: 1152 (不变, floor(5760/5))
- SHARE_RIGHT: 720 (不变, floor(3600/5))
- PROVINCE_AREA: 21.6 → 21 (floor(108/5))
- PROVINCE_TEAM: 28.8 → 28 (floor(144/5))
- CITY_AREA: 50.4 → 50 (floor(252/5))
- CITY_TEAM: 57.6 → 57 (floor(288/5))
- COMMUNITY: 115.2 → 115 (floor(576/5))
- 合计: 3171 → 3566 ✓
涉及服务:planting-service, admin-service, contribution-service
涉及前端:admin-web, mobile-app (Flutter)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:02:17 -08:00
hailin
5adcd023e6
fix(pricing): 修复 priceSupplement 在 Kafka 事件链中丢失的问题
...
## 问题描述
认种树动态定价涨价功能 (ed6b48562 ) 在 planting-service 的资金分配
中正确计算了 HQ_PRICE_SUPPLEMENT,但 priceSupplement 字段未随
Kafka 事件传递到 reward-service,导致 reward-service 的
calculateHqPriceSupplement 永远收到 priceSupplement=0,涨价部分
的总部奖励分配不会执行。
## 事件链路径 (修复前 → 修复后)
planting-service (contract-signing.service.ts)
↓ contract.signed / contract.expired
↓ 修复前: 不含 priceSupplement ❌
↓ 修复后: 携带 order.priceSupplement ✅
referral-service (contract-signing.handler.ts)
↓ planting.order.paid / planting.order.expired
↓ 修复前: 不含 priceSupplement ❌
↓ 修复后: 透传 eventData.priceSupplement || 0 ✅
reward-service (event-consumer.controller.ts)
↓ calculateHqPriceSupplement(priceSupplement)
↓ 修复前: 始终为 0,不分配 ❌
↓ 修复后: 收到实际值,正确分配给 S0000000001 ✅
## 修改文件
1. planting-service/src/infrastructure/kafka/event-publisher.service.ts
- ContractSigningEventData 接口新增 priceSupplement?: number 字段
2. planting-service/src/application/services/contract-signing.service.ts
- signContract(): publishContractSigned 时传递 order.priceSupplement
- handleExpiredTasks(): publishContractExpired 时传递 order.priceSupplement
3. referral-service/src/application/event-handlers/contract-signing.handler.ts
- ContractSigningEvent 接口新增 priceSupplement?: number 字段
- publishOrderPaidEvent(): 透传 priceSupplement 到 planting.order.paid
- publishOrderExpiredEvent(): 透传 priceSupplement 到 planting.order.expired
## 向后兼容
- priceSupplement 为可选字段 (?: number),默认 fallback 为 0
- 已存在的订单 priceSupplement=0,不影响现有分配逻辑
- reward-service event-consumer 已有 || 0 fallback 保护
## 验证方法
1. 设置 supplement > 0 后创建认种订单
2. 签署合同后检查 reward-service 日志是否有 HQ_PRICE_SUPPLEMENT 分配记录
3. 检查总部账户 S0000000001 是否收到 priceSupplement * treeCount 的入账
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 07:13:01 -08:00
hailin
ed6b48562a
feat(pricing): 认种树动态定价涨价系统(总部运营成本压力涨价)
...
基础价 15831 USDT/棵不变,新增 HQ_PRICE_SUPPLEMENT 加价项全额归总部(S0000000001)。
支持手动调价+自动周期涨价,所有变更可审计,移动端动态展示价格及涨价预告。
- admin-service: TreePricingConfig/ChangeLog 表 + Service + Controller + 定时任务
- planting-service: 正式认种和预种订单快照 priceSupplement,动态价格校验
- reward-service: HQ_PRICE_SUPPLEMENT 分配类型,涨价金额直接入总部账户
- admin-web: Settings 页面新增定价配置区间(手动调价/自动涨价/变更历史)
- mobile-app: TreePricingService + 动态价格加载 + 涨价预告展示
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 03:02:56 -08:00
hailin
023d71ac33
feat(authorization): 提高省团队权益初始激活门槛至3000棵
...
省团队(AUTH_PROVINCE_COMPANY)初始激活门槛: 500棵 → 3000棵
当前各角色初始激活门槛汇总:
- 社区(COMMUNITY): 100棵(上次已调整: 10→100)
- 市团队(AUTH_CITY_COMPANY): 500棵(上次已调整: 100→500)
- 省团队(AUTH_PROVINCE_COMPANY): 3000棵(本次调整: 500→3000)
- 市区域(CITY_COMPANY): 10000棵(未变)
- 省区域(PROVINCE_COMPANY): 50000棵(未变)
变更说明:
- 仅调整初始激活门槛,月度阶梯考核目标不变
- 已激活用户不受影响(祖父条款),仅对新申请的授权生效
- 数据库已有记录的 initialTargetTreeCount 保持旧值不变
- 需在服务器重新部署 authorization-service 后生效
修改文件:
- assessment-config.vo.ts: 核心门槛配置 500→3000
- authorization-application.service.ts: AUTH_PROVINCE_TARGET 常量及注释同步
- authorization.dto.ts: DTO 注释同步
- authorization.response.ts: ApiProperty 注释同步
- DEVELOPMENT_GUIDE.md: 文档说明同步
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:44:02 -08:00
hailin
577979bc83
chore: 同步线上构建版本号,避免服务器编译时版本号回退
...
- mining-app: 1.0.0+1 → 1.0.0+58(与线上已发布版本对齐)
- mobile-app: 2.0.0+1 → 2.0.0+357(与线上已发布版本对齐)
构建脚本会在此基础上自动递增,下次构建将从 59/358 开始。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:29:29 -08:00
hailin
a68fe5e999
feat(mining-app): 兑换页面"流通池"改名及"已销毁量"点击查看销毁明细
...
1. 将市场数据卡片中的"流通池"标签改名为"已分配积分股"
- 展示值不变,仍为 totalMined(全网已挖矿产出总量)
2. "已销毁量"新增点击交互,点击弹出"销毁明细"对话框:
- 系统总销毁量(blackHoleAmount):当前页面已展示的黑洞销毁总量
- 全网兑换销毁量(circulationPool):卖出交易实际产生的流通池金额
- 标签旁显示 info_outline 图标提示可点击
3. 重构 _buildMarketDataItem,拆分出 _buildMarketDataItemContent:
- _buildMarketDataItem:包含 Expanded 包裹,供普通数据项使用
- _buildMarketDataItemContent:纯内容组件,支持 showTapHint 参数
- 避免 Row > GestureDetector > Expanded 的布局冲突
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:55:37 -08:00
hailin
eacdfddff8
feat(authorization): 提高社区和市团队权益初始激活门槛
...
社区初始激活门槛: 10棵 → 100棵
市团队初始激活门槛: 100棵 → 500棵
变更说明:
- 仅调整初始激活门槛,月度考核目标不变(社区仍为10棵/月,市团队仍为100棵/月)
- 已激活用户不受影响(祖父条款),仅对新申请的授权生效
- 激活逻辑通过 AssessmentConfig 工厂方法动态获取门槛值,无需修改业务代码
- 数据库已有记录的 initialTargetTreeCount 保持旧值不变
修改文件:
- assessment-config.vo.ts: 核心门槛配置
- authorization-application.service.ts: 同步注释
- authorization-role.aggregate.spec.ts: 同步测试断言
- DEVELOPMENT_GUIDE.md: 同步文档描述
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:55:22 -08:00
hailin
eda39b982d
fix(mobile-app): 修复切换窗口期 NotificationBadge 混账号请求问题
...
问题(通过日志发现):
账号切换时存在一个 storage 空窗期(旧数据已清除、新数据尚未恢复完成)。
在此期间,NotificationBadgeNotifier 的 30s 定时器恰好触发,导致:
- _loadUnreadCount() 从 authProvider 内存读到旧账号 userSerialNum
- HTTP interceptor 从 storage 读到已恢复的新账号 accessToken
- 发出混账号请求:?userSerialNum=旧账号 + Authorization: Bearer 新账号token
日志证据:
_restoreAccountData() 执行期间出现
GET /notifications/unread-count?userSerialNum=D26022600000
Authorization: Bearer [D26022600001的token]
修复:
1. notification_badge_provider.dart
新增 stopAutoRefresh() 公开方法,取消 30s 定时器而不 dispose,
Provider invalidate 重建后会自动重启定时器。
2. account_switch_page.dart - _switchToAccount
在 onBeforeRestore 中补加:
ref.read(notificationBadgeProvider.notifier).stopAutoRefresh()
确保切换空窗期内 notificationBadge 定时器不触发。
同时移除 UI 层冗余的 saveCurrentAccountData() 调用——
switchToAccount() 内部已有此步骤,无需重复。
日志步骤从 [1/6]...[6/6] 更新为 [1/5]...[5/5],
并在 onBeforeRestore 注释中说明停止各定时器的原因。
切换空窗期现在所有定时器均已停止:
✓ walletStatusProvider (60s)
✓ pendingActionPollingService (4s)
✓ notificationBadgeProvider (30s) ← 本次新增
✓ TelemetryUploader (30s)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:09:37 -08:00
hailin
825c8a32e4
chore(mobile-app): 补全多账号切换流程的关键日志,便于验证与排查
...
切换流程中的每一步现在都有清晰的日志输出,方便通过 adb logcat
或 flutter logs 验证切换行为是否符合预期。
account_switch_page.dart - 三条路径全部覆盖:
_switchToAccount(切换账号):
[1/6] 保存当前账号数据
[2/6] 调用 switchToAccount()
[3/6] onBeforeRestore - 停止 walletStatus/pendingAction 轮询、暂停遥测
[4/6] invalidate authProvider / walletStatusProvider / notificationBadgeProvider
[5/6] 恢复遥测上传
[6/6] 导航到 ranking 页面
_addNewAccount(添加新账号):
[1/5] 保存当前账号数据
[2/5] 停止定时器
[3/5] 调用 logoutCurrentAccount()
[4/5] invalidate 三个 Provider
[5/5] 导航到 guide 页面
_deleteAccount(删除账号):
删除前打印目标账号,删除后打印剩余账号数
profile_page.dart - _performLogout(退出登录):
[1/4] 停止 walletStatus/pendingAction 轮询
[2/4] 调用 logoutCurrentAccount()
[3/4] invalidate 三个 Provider
[4/4] 导航到 guide 页面
每条关键操作完成后打印 ✓ 确认符号,便于逐步验证。
每条路径用 ========== 分隔符标识开始/完成,日志易于 grep 过滤。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:04:14 -08:00
hailin
e02bcf418c
fix(mobile-app): 升级弹窗禁止点击外部或返回键关闭,必须通过按钮操作
...
问题:
检测到新版本弹出升级对话框时,用户点击弹窗外部区域或按系统返回键
即可直接关闭弹窗,绕过升级提示,导致用户可能永远不会注意到更新。
修复:
对所有 3 个升级弹窗统一加两层防护:
- barrierDismissible: false — 禁止点击弹窗外部区域关闭
- PopScope(canPop: false) — 禁止系统返回键关闭
涉及弹窗:
1. self_hosted_updater.dart - _showSelfHostedUpdateDialog(自建APK更新)
2. self_hosted_updater.dart - _showMarketUpdateDialog(应用市场引导更新)
3. update_service.dart - _checkGooglePlayUpdate(Google Play 更新)
用户必须通过弹窗内按钮操作:
- 非强制更新:点击「稍后」/「暂时不更新」关闭,或点击「立即更新」开始更新
- 强制更新:只有「立即更新」按钮,无法跳过
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:01:52 -08:00
hailin
8f8a9230d0
fix(mobile-app): 修复多账号切换数据串号问题,完善存储隔离与状态重置
...
问题:
多账号切换时,前一个账号的推荐码、种植省市、缓存数据等会串到下一个账号,
同时定时器(钱包轮询、通知刷新、遥测上传)未正确停止/重启,
导致旧账号的 API 请求混入新账号上下文。
修复内容:
1. StorageKeys 补充种植省市常量(storage_keys.dart)
- 新增 plantingProvinceName/Code、plantingCityName/Code 4个常量
- 将硬编码的 key 统一收口,确保隔离列表引用一致
2. MultiAccountService 补全隔离列表(multi_account_service.dart)
- _accountSecureKeys 新增 inviterReferralCode(修复邀请码串号)
- _accountLocalKeys 新增 4个种植省市key + cachedAppAssets +
cachedCustomerServiceContacts(修复种植/缓存数据串号)
- switchToAccount() 新增 onBeforeRestore 回调参数,用于在
storage清空后、恢复新数据前 停止定时器
3. AccountSwitchPage 三层状态重置(account_switch_page.dart)
- _switchToAccount: onBeforeRestore 内停止 walletStatus 轮询、
pendingAction 轮询、telemetry 上传;返回后 invalidate
authProvider/walletStatusProvider/notificationBadgeProvider,
最后 resumeAfterLogin 恢复遥测
- _addNewAccount: 退出前停止定时器,退出后 invalidate Provider
4. ProfilePage 退出登录补全清理(profile_page.dart)
- _performLogout: 退出前停止 walletStatus/pendingAction 轮询,
退出后 invalidate 三个 Provider
5. 页面 key 统一引用 StorageKeys 常量
- planting_location_page.dart 和 authorization_apply_page.dart
将硬编码 key 替换为 StorageKeys.plantingXxx 常量
关键时序(switchToAccount 内部):
save → clear → onBeforeRestore(停timer) → restore → 返回
→ invalidate Provider(此时storage已恢复新数据)→ resume telemetry → navigate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 06:13:05 -08:00
hailin
a5a69645b4
fix(trading): 流通池改为显示已挖矿产出总量,解决显示为0的问题
...
前端"流通池"原来读取 circulationPool(交易流通池),因系统尚无卖出交易故为 0。
现改为读取 totalMined(全网已挖矿产出的积分股总量 = 用户已挖 + 系统已挖)。
后端新增 getTotalMinedFromMiningService() 方法,调用 mining-service 的
GET /mining/progress 接口获取 totalDistributed。
注意:价格公式中的 circulationPool 保持不变,仍用交易流通池参与计算。
新增的 totalMined 字段仅用于前端展示。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 02:54:26 -08:00
hailin
27db2a5aa2
fix(snapshot): 修复备份进行中被删除导致容器崩溃的问题
...
1. checkDone 中 statSync 加 try-catch,文件被删时 reject 而非 uncaught crash
2. 删除 API 禁止删除 RUNNING 状态的任务,返回 409 Conflict
3. compose 补充 restart: unless-stopped,防止异常退出后服务不可用
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 01:30:44 -08:00
hailin
18675f083c
fix(admin-web): 滚动区域改到 nav 内部,避免裁掉 toggle 按钮
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 06:55:32 -08:00
hailin
31d3eabcf8
fix(admin-web): 侧边栏菜单项过多时支持滚动,底部不再被截断
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 06:46:15 -08:00
hailin
e32ef9b9ff
fix(frontend): snapshot.types 补充 MPC_POSTGRES 类型和标签
...
后端新增了 MPC_POSTGRES 备份目标,前端 BackupTarget 类型和
BACKUP_TARGET_LABELS 缺少对应项导致复选框无法渲染。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:19:57 -08:00
hailin
716b37041e
fix(snapshot): PG_HOST 改用容器名 rwa-postgres 避免跨网络 DNS 冲突
...
snapshot-service 同时在 rwa-network 和 mpc-network 中,
服务名 "postgres" 在两个网络都存在导致解析到 mpc-postgres。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 04:02:01 -08:00
hailin
b6fb421316
feat(snapshot): 新增 MPC PostgreSQL 备份目标
...
MPC 系统使用独立的 PostgreSQL 实例 (mpc-postgres),之前不在备份范围内。
新增 MPC_POSTGRES handler,通过 MPC_PG_* 环境变量连接,snapshot-service
加入 mpc-network 实现跨 compose 网络访问。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 03:12:16 -08:00
hailin
26e55a649f
feat(snapshot): 进度显示增加文件大小信息,完成项显示 "完成 (29.4 MB)"
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 02:59:07 -08:00
hailin
ff28615fc3
fix(admin-web): Dockerfile 构建时传入 SNAPSHOT_SERVICE_URL build arg
...
Next.js rewrites 在构建阶段烘焙,运行时环境变量不生效
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 02:53:03 -08:00
hailin
de361e24f6
fix(infra): 网关 nginx 添加 snapshot-api 代理 + admin-web 配置 SNAPSHOT_SERVICE_URL
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 02:44:46 -08:00
hailin
30f1355bb4
fix(admin-web): snapshots 页面补上 PageContainer 包裹,恢复侧边栏
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 02:27:43 -08:00
hailin
08161c64d4
fix(snapshot): API 响应补上 progressMsg 字段
...
toSnapshotResponse 手动映射字段时遗漏了 progressMsg,
导致前端轮询拿不到 MB 进度消息。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 02:11:42 -08:00
hailin
8855491637
feat(snapshot): 进度精度升级 — Float百分比 + MB消息存DB
...
- schema: progress Int→Float,新增 progressMsg 字段
- PG handler: 百分比保留2位小数(toFixed(2)),不再 Math.floor
- orchestrator: 每2秒写DB时同时写 progressMsg (含MB信息)
- 前端: 百分比显示 toFixed(1),message 优先读 progressMsg
效果: 113GB库每次轮询进度条和MB数都有变化,不再卡在整数百分比
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 01:57:47 -08:00
hailin
9cbc0ba580
fix(snapshot): DATABASE_URL 改绝对路径,修复 SQLite 每次重建丢数据
...
Prisma 的 file:./data/snapshot.db 相对于 schema.prisma 所在目录
(/app/prisma/),实际创建在 /app/prisma/data/snapshot.db,不在
volume 挂载的 /app/data/ 下,导致容器重建后数据丢失。
改为 file:/app/data/snapshot.db 绝对路径,确保 SQLite 存入
volume 挂载目录,数据跨容器持久化。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 01:48:24 -08:00
hailin
7b7bfcac93
fix(snapshot): onModuleInit 增加扫描临时目录清理孤儿文件
...
SQLite 可能因重建丢失任务记录,导致 onModuleInit 仅靠查数据库
无法清理遗留的临时目录。新增逻辑:启动时扫描临时目录所有子目录,
若数据库中不存在对应任务则直接删除。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 01:45:24 -08:00
hailin
669a8a7248
fix(snapshot): 进度写 DB 改为每 2 秒一次,避免前端长时间显示 0%
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 01:40:32 -08:00
hailin
f14f685ea9
fix(snapshot): PG 进度改字节百分比 + Redis 备份防崩溃 + 启动清理遗留任务
...
- postgres-backup: 用 pg_database_size 做分母,统计 stdout 字节数算进度(与 pv 方案一致)
- redis-backup: BGSAVE 后先 copyFileSync 到临时目录再打包,防止 tar-stream Size mismatch 崩溃
- orchestrator: onModuleInit 清理遗留 RUNNING 任务,标记 FAILED 并删除临时文件
- docker-compose: 临时文件改挂宿主机 /tmp 目录,方便手动清理
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 01:30:52 -08:00
hailin
38efa891b8
fix(snapshot): Dockerfile 添加 SNAPSHOT_SERVICE_URL build ARG
...
Next.js rewrites 在 build 时烘焙到 routes-manifest.json,
运行时环境变量无法覆盖。需要通过 Docker build ARG 在构建时传入。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 00:43:33 -08:00
hailin
ee94f1420d
fix(snapshot): 前端 API 改走 Next.js rewrites 代理 + WebSocket 改 REST 轮询
...
- snapshot.api.ts: 从直连 localhost:port 改为 /api/snapshots/* 走 Next.js 代理
- next.config: 两个前端都添加 /api/snapshots/:path* → snapshot-service 代理规则
- docker-compose.2.0-snapshot.yml: overlay 中追加 mining-admin-web 的 SNAPSHOT_SERVICE_URL
- useSnapshotWebSocket → useSnapshotPolling: 2秒轮询 GET /snapshots/:id 获取进度
- 移除 socket.io-client 依赖(Next.js standalone 不支持 WebSocket proxy)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 00:37:41 -08:00
hailin
9a88fb473a
fix(snapshot): Dockerfile 安装 postgresql-client-16 匹配目标 PG 版本
...
bookworm 默认 postgresql-client 是 15,目标数据库是 PG16
添加 PGDG 源安装 postgresql-client-16
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:20:45 -08:00
hailin
470dc1ccd0
fix(snapshot): PostgreSQL 备份从 pg_basebackup 改为 pg_dumpall
...
pg_basebackup -D - -Ft -z 在 PG15 中不支持同时 WAL streaming
改用 pg_dumpall | gzip 逻辑备份,更轻量且不需要 replication 权限
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:18:52 -08:00
hailin
a4689d5e8b
fix(snapshot): 修复 listSnapshots 分页参数 NaN 问题
...
NestJS @Query() 返回 string,需手动 parseInt 再传给 Prisma
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:15:26 -08:00
hailin
b8b4305ea5
fix(snapshot): Dockerfile 添加 ca-certificates 修复 mc 下载 SSL 错误
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:11:07 -08:00
hailin
685aced4e4
fix(snapshot): 修复 6 个 TypeScript 严格模式编译错误
...
- infrastructure.module: handler union type 修正
- minio-storage.adapter: obj.name 可选属性安全检查
- minio-backup.handler: listAllObjects 返回 string[] 避免 BucketItem.name 可选问题
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:10:08 -08:00
hailin
18c9f8b389
chore(snapshot): 添加 snapshot-service package-lock.json
...
Docker build 的 npm ci 需要 lock 文件。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:06:42 -08:00
hailin
ef2f0f67bf
chore: 更新前端 package-lock.json 同步 socket.io-client 依赖
...
添加 socket.io-client 到 package.json 后未同步 lock 文件,
导致 Docker 构建时 npm ci 报 EUSAGE 错误。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 21:56:28 -08:00