Commit Graph

2609 Commits

Author SHA1 Message Date
hailin 3a84315b64 fix(mobile-app): verifyLoginPassword 读取响应体 valid 字段
后端 POST /user/verify-password 返回 { valid: bool },不用 HTTP 状态码区分,
修正响应解析逻辑。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 05:53:16 -08:00
hailin 41fa6349bd feat(mobile-app): 认种确认前新增登录密码校验弹窗
- 新增 PasswordVerifyDialog 弹窗,风格与主体 App 一致
- account_service 新增 verifyLoginPassword() 调用 POST /user/verify-password
- planting_location_page 在 PlantingConfirmDialog 确认后插入密码校验步骤

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 05:51:47 -08:00
hailin 8de92f2511 fix(mining-app): 已解锁上 null 时显示 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 05:41:00 -08:00
hailin 98c898769f fix(mining-app): 已解锁上层数 = unlockedBonusTiers × 5
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 05:40:00 -08:00
hailin 353299fd75 fix(mining-app): 已解锁上层数改为动态数据,替换硬编码的 15
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 05:13:10 -08:00
hailin b67dfa0f4c fix(planting-service+app): 合并列表补充 totalPortions,前端显示实际份数而非订单数
- getMerges 批量查来源订单 portionCount,按合并分组求和后返回 totalPortions
- 预种明细合并卡片改用 totalPortions 显示份数,fallback 才用订单数

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 08:58:45 -08:00
hailin 1f5bb62805 fix(contribution-service): 合并完成后写入 synced_adoptions 记录以解除卖出限制
swapContributionForMerge 在事务内新增步骤 9f:
- 写入 synced_adoptions(original_adoption_id = 20B + mergeId)
- 供 SellRestrictionService.isRestricted 的 has_real_tree 判断使用
- upsert 保证幂等,contributionDistributed 由调用方置 true

Bug: 合并已完成(contribution_records 正确),但 isRestricted 始终
返回 true,因为 synced_adoptions 中没有 20B+ 记录(virtualAdoption
仅用于计算,未持久化)。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 08:25:15 -08:00
hailin 94792f56ea fix(contribution-service): 预种算力除数从 5 改为 10,冻结阈值同步更新
- PRE_PLANTING_PORTION_DIVISOR: 5 → 10(每份算力 = 1棵树的 1/10)
- 冻结条件:totalPortions < 5 → < 10
- 解冻条件:totalPortions >= 5 → >= 10
- 同步更新所有相关注释和日志文案

注:2026-03-02 前已分配的份额算力记录(按 1/5 计算)不追溯修正。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 08:04:42 -08:00
hailin 2b07219046 fix(mining-app): sellRestrictionProvider 绑定用户身份,避免跨账号缓存污染
[问题] sellRestrictionProvider 未绑定用户身份。若先用预种账号(isRestricted=true)
测试,再切换为正式认种账号,Riverpod keepAlive 缓存会在 2 分钟内残留上一个用户
的 true 值,导致正式认种用户错误看到预种卖出限制提示。

[修复] 在 provider 内 watch userNotifierProvider.select((s) => s.accountSequence),
accountSequence 变化(登录/切换账号)时 provider 自动失效并重新请求。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 07:29:12 -08:00
hailin 59097203ae fix(app): 预种计划入口移至自助申请授权页市团队下方
## 问题
上一版错误将「申请预种计划」入口放在「我」页面主区域,
实际需求是放在「自助申请授权」页的「申请市团队」卡片下方。

## 改动内容

### authorization_apply_page.dart
- import route_paths.dart
- `_buildAuthorizationTypes()` 将 `.map()` 展开为 for 循环,
  在 AuthorizationType.cityTeam item 渲染完后插入 `_buildPrePlantingEntry()`
- 新增 `_buildPrePlantingEntry()` 方法:
  · 样式:金色(0xFFD4AF37)卡片,左侧图标圆角背景 + 右侧箭头,
    副标题「购买预种份额 · 查看明细 · 团队预种统计」
  · 点击跳转 RoutePaths.prePlantingHub(预种计划 Hub 页)

### profile_page.dart(撤销上一版在主页的改动)
- 移除 `bool _isPrePlantingActive` 状态变量
- 移除 `_loadPrePlantingConfig()` 方法及 initState 调用
- 移除布局中的预种入口按钮区块
- 移除 `_buildPrePlantingEntryButton()` 方法

Hub 页面(pre_planting_hub_page.dart)和路由定义保持不变,
入口位置从「我」主页改为自助申请授权页的市团队下方。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 07:03:59 -08:00
hailin 8b48d80cd4 feat(app): 预种计划三按钮合并为单入口 Hub 页面
## 背景
「我」页面原有三个并排的预种按钮(预种购买 / 预种明细 / 团队预种),
三等分宽度导致文字拥挤,视觉层级也不清晰。

## 改动内容

### 新增文件
- `pre_planting_hub_page.dart`
  预种计划 Hub 汇总页,竖排展示三个功能入口卡片:
  · 预种购买(金色)→ /pre-planting/purchase
  · 预种明细(棕色)→ /pre-planting/position
  · 团队预种(绿色)→ /pre-planting/team
  页面为纯导航页,无需加载数据;风格与 pre_planting_purchase_page
  一致(渐变背景、金色「← 返回」Header、Inter 字体)。

### 修改文件
- `route_paths.dart` / `route_names.dart`
  新增路由常量 prePlantingHub = '/pre-planting/hub'

- `app_router.dart`
  注册 GoRoute(path: prePlantingHub) → PrePlantingHubPage
  新增对应 import

- `profile_page.dart`
  · 删除 `_buildPrePlantingButtons()`(三按钮并排 Widget)
  · 删除已无用的三个导航方法:
    _goToPrePlantingPurchase / _goToPrePlantingPosition / _goToTeamPrePlanting
  · 新增 `_buildPrePlantingEntryButton()`:
    满宽 48px 按钮,文字「申请预种计划」,点击跳转 Hub 页
  · 布局调用处改为 _buildPrePlantingEntryButton()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 06:42:50 -08:00
hailin 979ba379c1 fix(text): 预种合并份数从5份改为10份
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 06:18:12 -08:00
hailin 867d4853ae fix(trading-service): 补充 CONTRIBUTION_SERVICE_URL 环境变量
trading-service 调用 contribution-service 做预种卖出限制检查时,
因缺少 CONTRIBUTION_SERVICE_URL 导致 fallback 到 localhost:3020,
在 Docker 网络中无法连通,造成 fail-open 放行所有卖出。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 06:09:47 -08:00
hailin 4904337e97 revert(app): 回滚 C2C 页面的卖出限制
C2C 卖出的是积分值,不是积分股,不受预种卖出限制约束。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 05:57:24 -08:00
hailin ee734fb7b9 feat(trading+app): 预种卖出限制 — 前端 UI 禁用 + 后端查询端点
trading-service:
- asset.controller.ts: 新增 GET /asset/sell-restriction,供 mobile app 查询当前用户限制状态
- application.module.ts: 导出 TradingSellRestrictionService

mining-app:
- api_endpoints.dart: 新增 sellRestriction 端点常量
- trading_remote_datasource.dart: 新增 getSellRestriction()(fail-open)
- trading_repository.dart/impl: 新增接口与实现
- trading_providers.dart: 新增 sellRestrictionProvider(2分钟缓存,fail-open)
- trading_page.dart: 卖出限制时显示红色提示文字并禁用"确认交易"按钮
- c2c_publish_page.dart: 发布卖出广告时显示红色提示文字并禁用发布按钮

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 05:51:56 -08:00
hailin ac3adfc90a feat(pre-planting): 新增预种积分股卖出限制(方案B纯新增)
限制仅有预种份数(未合并成棵)的用户卖出积分股,
直到用户完成首次预种合并后方可卖出。

=== 改动范围(全部 2.0 系统,纯新增)===

contribution-service:
- prisma/pre-planting/schema.prisma: 新增 PrePlantingSellRestrictionOverride 模型
- migrations/20260304000000: 对应建表 SQL
- src/pre-planting/application/services/sell-restriction.service.ts: 核心判断逻辑
  isRestricted = has_pre_planting_marker AND !has_real_tree AND !admin_override
- src/api/controllers/pre-planting-restriction.controller.ts: 暴露内部接口
  GET  /api/v2/pre-planting/sell-restriction/:accountSequence (@Public)
  POST /api/v2/pre-planting/sell-restriction/:accountSequence/unlock (@Public)
- src/api/api.module.ts: 注册新 controller 和 SellRestrictionService

trading-service:
- src/application/services/sell-restriction.service.ts: HTTP + Redis 缓存(TTL 60s)
  fail-open:contribution-service 不可用时允许卖出,保障业务连续性
- src/application/services/order.service.ts: 卖单前增加限制检查(4行)
- src/application/application.module.ts: 注册 TradingSellRestrictionService

mining-admin-service:
- src/application/services/pre-planting-restriction.service.ts: 代理接口 + 审计日志
  每次管理员解除操作均写入 AuditLog,保证严格可追溯性
- src/api/controllers/pre-planting-restriction.controller.ts:
  GET  /pre-planting-restriction/:accountSequence
  POST /pre-planting-restriction/:accountSequence/unlock
- api.module.ts / application.module.ts: 注册新服务和接口

mining-admin-web:
- users.api.ts: 新增 getPrePlantingRestriction / unlockPrePlantingRestriction
- use-users.ts: 新增 usePrePlantingRestriction / useUnlockPrePlantingRestriction hooks
- users/[accountSequence]/page.tsx: 受限时在基本信息卡显示红色警告 + 解除按钮

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 05:04:57 -08:00
hailin 8fcfec9b65 fix(contribution): backfill 强制重算 unlock status,修复预种用户层级卡在5级的问题
问题根因:直推用户先买预种导致 directReferralAdoptedCount 已累加到正确值(如5),
但 markAsAdopted() 随后被调用时硬编码 level=5/bonus=1,覆盖了正确的解锁状态。
之后 backfill 因 count 未变(5>5=false)永远不触发重算,level 永久卡死。

修复:updateAccountUnlockStatus 改用 setDirectReferralAdoptedCount() 替代
incrementDirectReferralAdoptedCount 循环,无论 count 是否变化都强制调用
updateUnlockStatus() 重算 unlockedLevelDepth 和 unlockedBonusTiers。

同时为 getDirectReferralAdoptedCount 补充注释,说明常规认种和预种均按人头计。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 23:16:47 -08:00
hailin 728728bee3 fix(admin-service): 个人/团队认种数量改为统计棵数而非订单数
【问题】
getUserList 及推荐树节点中「个人认种」「团队认种」显示的是订单条数
(_count: { id }),而非实际认种棵数。一笔订单可认种多棵,导致多棵
合并下单的用户数量严重偏低。

【修复】
将以下方法中的所有 count(orders) 改为 sum(treeCount):
- getPersonalAdoptionCount   — 用户详情页个人认种数
- getTeamStats               — 用户详情页团队认种数
- getBatchUserStats          — 用户列表批量统计(个人/团队/省/市认种数)
- getAncestors               — 推荐树祖先节点认种数
- getDirectReferrals         — 推荐树直推节点认种数

【影响范围】
仅影响 admin-web 管理后台的展示数据,不涉及业务逻辑和数据存储。
省市认种百分比计算基准同步修正(teamAdoptions 也改为棵数),
比例结果不变,但基数更准确。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 22:29:32 -08:00
hailin f8f37a2e33 fix(admin-web): 推荐树节点「团队预种」显示时减去本人份数
后端返回的 teamPrePlantingPortions 包含该节点自身的 selfPrePlantingPortions,
导致管理后台推荐树中「团队预种」数量比实际多出本人的份数。

修复:推荐树展示层(祖先节点 + 直推/展开节点两处)统一改为:
  团队预种显示值 = teamPrePlantingPortions - selfPrePlantingPortions

用户详情卡片顶部的原始统计数字保持不变(供管理员核查原始数据)。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 22:19:05 -08:00
hailin 3f4b22b013 docs(contribution): add detailed comments for backfill task and findAccountsWithIncompleteUnlock
Explain the starvation root cause, unlock rules, pre-planting user scenario,
and future scalability considerations (cursor-based pagination).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 21:58:52 -08:00
hailin 2565fa8259 fix(contribution): process all incomplete-unlock accounts in backfill (remove 100-limit)
Previous 100-account batch caused starvation: accounts at positions 101+
(including recent pre-planting users) were never processed. Remove limit
to process all eligible accounts in each 10-minute run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 21:55:46 -08:00
hailin 551723fe82 fix(contribution): remove redundant snapshotDate from GetBatchRatiosRequest query DTO
The date is already read from URL path param @Param('date'), not query string.
Having it as required in the query DTO caused 400 Bad Request on ratios endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 09:06:53 -08:00
hailin fb4e52c0de fix(contribution): add @Public() to getBatchRatios endpoint for service-to-service calls
mining-service calls this endpoint without JWT token during DailySnapshot
full sync, causing 401 Unauthorized. Mark it as public since it's internal data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 08:35:55 -08:00
hailin 85c20adb0b fix: add libc6-compat to builder stage for SWC binary
The Next.js SWC binary requires libc6-compat on Alpine Linux.
It was only installed in the deps stage but not the builder stage,
causing build failures on fresh (no-cache) Docker builds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 08:34:46 -08:00
hailin a392f708a7 fix(mining): 修复 DailySnapshot API 路径 v1→v2 + deploy-mining.sh 默认 standalone
- fetchContributionRatios URL 从 /api/v1/ 改为 /api/v2/ 与 contribution-service 的 globalPrefix 匹配
- deploy-mining.sh 默认部署模式从 shared 改为 standalone

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 08:27:57 -08:00
hailin 54e22b4709 feat: add IT0 API URL to mobile-upgrade production config
Add NEXT_PUBLIC_IT0_API_URL=https://it0api.szaiai.com to enable
the IT0 App version management in the mobile-upgrade admin panel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 08:19:21 -08:00
hailin 33ae08c90f feat(mobile-upgrade): add IT0 App version management support
Add IT0 App as the third application in the mobile-upgrade frontend,
connecting to IT0's new version-service backend.

Changes:
- api-client.ts: added 'it0' to AppType union, APP_CONFIGS entry
  pointing to NEXT_PUBLIC_IT0_API_URL (default: it0api.szaiai.com),
  exported it0ApiClient instance
- version-repository-impl.ts: added it0VersionRepository instance,
  updated constructor client selection for it0, updated
  getVersionRepository() factory with switch/case
- page.tsx: added IT0 App to APP_LABELS, added emerald green toggle
  button in app selector
- upload-modal.tsx: fixed existing bug where parsePackage() was
  hardcoded to use mobile repository regardless of selected app;
  now uses getVersionRepository(appType) dynamically

Backend: IT0 version-service at it0api.szaiai.com/api/v1/versions
Env var: NEXT_PUBLIC_IT0_API_URL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 07:49:37 -08:00
hailin 2b2e1efc7a fix(mining): 修复挖矿分配并发覆盖贡献值同步的 Lost Update 问题
挖矿分配每秒运行的 save() 无条件写回所有字段(含 totalContribution),
导致贡献值同步刚更新的正确值被立即覆盖回旧值。
同时修复 DailySnapshot 全量同步一直 synced 0 accounts 的安全网失效问题。

- repository save() 增加 skipContributionUpdate 选项
- 挖矿分配路径传入 skipContributionUpdate: true
- contribution-service DailySnapshot 事件 payload 补全字段
- mining-service 适配字段名差异并修复 API 解析 bug

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 07:24:52 -08:00
hailin 92c305c749 fix(mobile-app): 待签署合同页文案改为"10份预种份额已合并为1棵树"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 03:40:49 -08:00
hailin 2f78899ceb fix(mobile-app): 修复待签署合同页和持仓页硬编码"5份"的问题
预种方案已从5份/棵调整为10份/棵,但以下两处文案仍硬编码为5:

1. 待签署合同页 (pending_contracts_page.dart)
   - 原: "5 份预种份额已合并,请签署合同以开启挖矿"
   - 改: "{订单数} 笔预种订单已合并为 {树数} 棵树,请签署合同以开启挖矿"
   - 使用 merge.sourceOrderNos.length 和 merge.treeCount 动态显示

2. 持仓页空状态 (pre_planting_position_page.dart)
   - 原: "累计 5 份后将自动合成 1 棵树"
   - 改: "累计 $_portionsPerTree 份后将自动合成 1 棵树"
   - 使用已有常量 _portionsPerTree=10

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 03:30:42 -08:00
hailin 55f81ff329 fix(mobile-app): 修复多个页面时间显示为UTC而非北京时间的问题
后端数据库统一存储UTC时间,前端展示时需调用 .toLocal() 转换为
设备本地时区(中国地区即 UTC+8 北京时间)。

以下5个页面的 _formatDateTime() 方法缺少 .toLocal() 转换,
导致页面显示的时间比北京时间慢8小时:

- 预种合并详情页 (pre_planting_merge_detail_page.dart)
  → 合并时间、签署时间、挖矿开启时间
- 预种持仓页 (pre_planting_position_page.dart)
  → 购买时间、合并时间
- 合同签署页 (contract_signing_page.dart)
  → 合同签署时间
- 转让列表页 (transfer_list_page.dart)
  → 转让创建时间
- 转让详情页 (transfer_detail_page.dart)
  → 转让各状态时间

修复方式:在格式化前统一调用 dt.toLocal() 将UTC转为本地时区。
后端和数据库保持UTC不变,仅前端展示层做时区转换。

注:ledger_detail_page.dart 已使用 DateTimeUtils.formatDateTime()
(内含 .toLocal()),无需修改。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 03:11:48 -08:00
hailin 532be9a561 fix(pre-planting): 合并详情显示实际份数和金额,不再硬编码
问题:合并详情页"合并份数"显示订单条数(7)而非实际份数(10),
"总价值"硬编码 订单数×1887,每笔订单金额也硬编码 1,887。

修复:
后端 getMergeDetail:
  - 新增 sourceOrders[] 含每笔订单的 portionCount + totalAmount
  - 新增 totalPortions(总份数)和 totalAmount(总金额)

前端 PrePlantingMerge model:
  - 新增 MergeSourceOrder 类
  - 新增 sourceOrders/totalPortions/totalAmount 字段

前端合并详情页:
  - "合并份数"用 totalPortions 替代 sourceOrderNos.length
  - "总价值"用 totalAmount 替代硬编码计算
  - 来源订单列表显示每笔实际金额和份数(多份时显示"N份"前缀)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:58:34 -08:00
hailin a8e06e2eda fix(pre-planting): 合并逻辑改为按份数累计,支持多份订单合并
问题:用户一笔订单可购买多份(portionCount>1),但 performMerge
按订单条数校验是否够 10 条,导致 6 笔订单共 10 份却报错
"不足 10 笔已支付订单进行合并"。

修复:
- performMerge: 遍历 PAID 订单累加 portionCount 直到凑满 10 份
- findPaidOrdersByUserId: 去掉 limit 参数,获取所有 PAID 订单
- PrePlantingMerge.create: 去掉 sourceOrderNos.length === 10 校验
  改为 length > 0(份数校验已在 performMerge 完成)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:44:35 -08:00
hailin d7f7d7082d fix(pricing): 预种定价API常量同步 — 3566→1887, /5→/10, 正式认种15831不变
admin-service tree-pricing.service.ts:
  - BASE_PORTION_PRICE: 3566 → 1887
  - supplement 除数: /5 → /PORTIONS_PER_TREE(10)
  - BASE_PRICE 保持 15831(正式认种价格不变)
  - 移除 updateSupplement 中重复声明的 BASE_PRICE 局部变量

planting-service tree-pricing-admin.client.ts:
  - fallback basePortionPrice/totalPortionPrice: 3566 → 1887

mobile-app tree_pricing_service.dart:
  - 修正上次commit误改的 basePrice/totalPrice fallback: 18870 → 15831
  - basePortionPrice/totalPortionPrice 保持 1887

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:56:21 -08:00
hailin 1e31d6d863 feat(mining-app): 市场数据3个小数字段显示后端返回的完整精度
原先 formatCompact/formatWithCommas 内部用 double 运算,double 有效精度
仅约15-16位,对大数值(如98亿)除以1e8后小数部分会丢失精度。
后端 3 个字段均为 Decimal(30,8),通过 toFixed(8) 返回 8 位小数。

新增 formatCompactFull / formatWithCommasFull 函数:
- _shiftDecimalLeft: 纯字符串小数点移位,不经 double 转换,零精度损失
- _addCommasFullPrecision: 整数部分加千位逗号 + 小数部分原样保留
- formatCompactFull: 万/亿缩写 + 完整后端精度(替代 formatCompact)
- formatWithCommasFull: 逗号分隔 + 完整后端精度(替代 formatWithCommas)

兑换页 (trading_page.dart) 修改:
- 剩余积分股: formatCompact(precision:4) → formatCompactFull (8位小数)
- 已分配积分股: formatCompact(precision:2) → formatCompactFull (8位小数)
- 积分股池: formatWithCommas → formatWithCommasFull (8位小数)
- 已销毁量: 保持 formatIntWithCommas 整数显示不变

贡献值页 (contribution_page.dart) 修改:
- 100亿销毁剩余量: formatAmount(4位) → formatCompactFull (完整精度)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:58:58 -08:00
hailin af2afeda56 fix(admin-web): 修复申请照片页面响应解包多余一层导致数据为空
apiClient 响应拦截器已返回 response.data,页面不应再取 .data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:15:30 -08:00
hailin a801a46e76 fix(admin): 修复授权照片代理未解包全局响应拦截器的问题
authorization-service 全局拦截器将响应包装为 {success, data, timestamp},
代理服务需要从 response.data.data 取实际数据,而非 response.data。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:50:52 -08:00
hailin 0576733579 fix(mining-app): 统一贡献值页与兑换页的剩余积分股计算方式
贡献值页原先使用 sharePoolBalance API (Pool A + Pool B 余额) 显示剩余积分股,
兑换页使用公式 totalShares - totalMined - blackHoleAmount 计算。
两者显示结果不一致 (差异约 228,343 积分股)。

根本原因调查:
- mining-wallet-service 的 Kafka 消费者在服务重启期间丢失了部分事件
- Pool B 仅处理了 15.7% 的挖矿分配事件 (28,261/180,497)
- Pool A 遗漏了 66,591 笔销毁扣减事件
- 池账户余额是通过 Kafka 事件维护的记账台账,存在消费遗漏
- totalShares - totalMined - blackHoleAmount 公式基于实际挖矿和销毁数据计算,是数学上的 ground truth
- 实际用户分配和销毁均 100% 准确,仅池账户记账有偏差

修复方案:
- 贡献值页改用 marketOverviewProvider (同兑换页)
- 使用公式 totalShares - totalMined - blackHoleAmount 计算剩余积分股
- 两个页面现在显示完全一致的数据

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:28:55 -08:00
hailin a7dd926877 fix(mining-app): 修复分配记录 distributionMinute 时间仍显示UTC的问题
上次修复使用了 DateTime.parse(t).toLocal(),但 distributionMinute
字符串不带 Z 后缀(如 "2026-03-03 05:09:00"),Dart 的 DateTime.parse
在无时区标识时默认当作本地时间处理,导致 .toLocal() 无效。

修复:解析前追加 Z 后缀,强制标记为 UTC,使 .toLocal() 正确转换
为北京时间(UTC+8)。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 21:26:47 -08:00
hailin 1621b75a47 feat(admin): 引荐关系树节点增加个人/团队预种份数展示
后端:
- ReferralNodeDto 新增 selfPrePlantingPortions, teamPrePlantingPortions
- user-detail.controller: getReferralTree 中并行调用
  ReferralProxyService 批量获取所有节点的预种统计
  (当前用户用 getPrePlantingStats,祖先+下级用 batchGetPrePlantingStats)

前端:
- ReferralNode 类型新增两个预种字段
- 引荐关系树节点(祖先链 + 递归展开节点)在"本人认种/团队认种"
  下方新增一行"个人预种: X份 / 团队预种: Y份"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:16:02 -08:00
hailin eb425b0f92 fix(referral): 团队预种总量排除自己的预种份数
TeamStatistics.teamPrePlantingPortions 包含用户自身的预种量,
但"团队预种"应只统计伞下成员。返回时减去 selfPrePlantingPortions。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:03:08 -08:00
hailin b59d5bda2d fix(mining-app): 分配记录 distributionMinute 时间转换为北京时间
_shortTime 中 DateTime.parse() 后未调用 .toLocal(),
导致第一行的分配时间仍显示 UTC 而非北京时间(与第三行 createdAt 不一致)。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:52:03 -08:00
hailin 40731c08ea fix(mining-app): 分配记录时间压缩防溢出 + 剩余积分股扣除销毁量
1. 分配记录页面:
   - 时间格式从完整时间戳压缩为 "YY/MM/DD HH:mm:ss",节省空间
   - 积分股字号从18缩至13,字体改为monospace便于对齐
   - 外层加 Flexible 防止超出屏幕右边界

2. 兑换页面剩余积分股:
   - 公式从 totalShares - totalMined
     改为 totalShares - totalMined - blackHoleAmount
   - 即:总量 - 已分配 - 已销毁 = 真正的剩余可分配量

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:27:05 -08:00
hailin 789a703ec8 fix(mining-app): 修复剩余积分股显示精度不足导致与总量看起来一样
问题:totalShares=100.02亿,totalMined≈18万,相减后≈100.0182亿,
但 formatCompact 在亿级别只保留2位小数,四舍五入后仍显示"100.02亿",
与总量无法区分。

修复:
- formatCompact 新增可选 precision 参数(默认2,向后兼容)
- 剩余积分股使用 precision=4,显示为"100.0182亿",可见差异

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:25:31 -08:00
hailin ab78086f1e feat(mining-app+admin): 挖矿记录积分股小数精度统一提升至13位
需求:每次预种叠加后的积分股变化极小,原有精度(APP 4位/后台 8位)
无法有效区分,需提升至13位小数以便查看细微变化。

修改范围:
- mining-admin-web: 分配记录 shareAmount 8→13 位小数
- mining-admin-web: 补发记录 amount 8→13 位小数
- mining-app: 补发记录 amount 4→13 位小数
  (mining-app 分配记录已在上一次提交中修改)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:09:20 -08:00
hailin dc27fe9e44 fix(mining-app): 修复 ProfilePage._buildUserHeader 缺少 ref 参数导致编译失败
_buildNotificationIcon 需要 WidgetRef ref 来 watch 未读通知数,
但 _buildUserHeader 方法签名未声明该参数,导致编译错误:
"The getter 'ref' isn't defined for the type 'ProfilePage'"

修复:将 ref 从 build() 透传给 _buildUserHeader()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:05:36 -08:00
hailin 2706eef54f feat(mining-app): 兑换页"总积分股"改为"剩余积分股",分配记录积分股精度提升至13位
1. 兑换页面(trading_page):
   - 市场数据卡片标签从"总积分股"改为"剩余积分股"
   - 值的计算从直接显示 totalShares(100.02亿)改为
     totalShares - totalMined(总量 - 全网已分配量),
     使用 Decimal 精确运算避免浮点误差

2. 分配记录页面(mining_records_page):
   - 每条分配记录的积分股显示从 formatAmount(4位小数)
     改为 formatDecimal(value, 13),展示完整13位小数精度

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:04:07 -08:00
hailin 5e05e336f7 fix(mining-admin): add @map for targetType column in Notification schema
Missing @map("target_type") caused Prisma to look for camelCase column
name instead of the snake_case column created in migration SQL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:55:00 -08:00
hailin e68b5aa3d9 feat(mining-admin): add Prisma migration for notification tables
Create notifications, notification_reads, notification_user_targets
tables with indexes and unique constraints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:48:17 -08:00
hailin 5ee94b3672 fix(notifications): NotificationPriority 类型从 string 改为 Prisma 枚举
修复后端编译错误: priority 字段类型应使用 @prisma/client 的
NotificationPriority 枚举而非 string。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:38:25 -08:00