Commit Graph

2588 Commits

Author SHA1 Message Date
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
hailin 7c781c7d62 feat(notifications): 2.0系统通知弹窗功能(后端+管理端+APP端)
复制1.0通知系统架构到2.0系统,实现完整的通知推送功能:

后端 (mining-admin-service):
- Prisma Schema: 添加 Notification/NotificationRead/NotificationUserTarget 表
- NotificationService: 完整 CRUD + 移动端通知查询/已读标记
- AdminNotificationController: 管理端通知 CRUD API
- MobileNotificationController: 移动端通知列表/未读数/标记已读 API

管理端 (mining-admin-web):
- 通知管理页面: 列表/筛选/新建/编辑/删除 Dialog
- 支持类型/优先级/目标用户/强制弹窗/发布时间等完整配置
- 侧边栏添加"通知管理"入口

APP端 (mining-app):
- NotificationService: 通知API服务(经Kong网关路由)
- NotificationBadgeProvider: 30秒轮询未读数量+生命周期监听
- ForceReadNotificationDialog: 强制阅读弹窗(橙色主题,逐条查看+确认)
- NotificationInboxPage: 通知收件箱(支持dark/light主题)
- MainShell: 添加强制弹窗检查(启动+前台恢复,60秒冷却)
- ProfilePage: 用户头部添加通知图标+未读角标

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:35:16 -08:00
hailin 59f7bdc137 feat(admin): 功能8修正 — 自助申请照片独立展示页(纯新增方案)
用户在 App 自助申请社区/市团队/省团队授权时上传的办公室照片,
之前错误放在授权管理页且因 CDC 未同步导致全显示"-"。
本次采用纯新增方案:绕过 CDC,通过内部 HTTP API 直连
authorization-service 源头数据库读取照片,保证数据 100% 准确。

=== 数据流 ===
admin-web 新页面 → admin-service 新 Controller
  → authorization-service 新 Internal API
  → authorization_roles 表 (源头, officePhotoUrls 字段)

=== 后端 — authorization-service ===
- 新建 internal-self-apply-photos.controller.ts
  GET /authorization/self-apply-photos?page=1&limit=20&roleType=COMMUNITY
  使用 $queryRaw 查询 office_photo_urls != '{}' 的记录
  支持 roleType 筛选 + 分页
- index.ts 新增 export, app.module.ts 注册 controller

=== 后端 — admin-service ===
- 新建 authorization/authorization-proxy.service.ts
  axios 代理调用 authorization-service 内部 API
  批量查 user_query_view 补充 nickname + avatarUrl
- 新建 api/controllers/authorization-photos.controller.ts
  GET /admin/authorization-photos (admin-web 调用)
- app.module.ts 注册 controller + provider
- docker-compose.yml 追加 AUTHORIZATION_SERVICE_URL 环境变量

=== 前端 — admin-web ===
- 新建 authorization-photos/ 页面 (page.tsx + SCSS)
  表格展示:头像、昵称、账户序列号、授权类型、地区、照片数、申请时间
  点击照片弹出 Modal 网格 → 点击单张弹出全屏 Lightbox
  支持 roleType 筛选 + 分页
- Sidebar.tsx 追加"申请照片"菜单项 (紧随"授权管理"之后)
- endpoints.ts 追加 SELF_APPLY_PHOTOS 端点
- authorization/page.tsx 移除"申请照片"列、photo modal、lightbox
- authorization.module.scss 清理照片相关样式

=== 风险 ===
- CDC 链路: 零修改
- 现有 API: 零冲突 (新 controller 独立文件)
- 2.0 系统: 零影响
- 所有操作均为只读查询

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:08:32 -08:00
hailin 41818eb8e2 feat(mining-admin-web): 添加全局兑换记录页面
后端(trading-service):
- OrderRepository 新增 findAllOrders/findAllTrades 全局查询方法
- AdminController 新增 GET /admin/orders 和 GET /admin/trades 端点
  支持 type/status/source/search/日期范围筛选 + 分页

前端(mining-admin-web):
- 新增 /exchange-records 页面,包含「订单记录」和「成交明细」两个 Tab
- 订单 Tab: 支持按类型/状态/来源筛选,显示订单号/账号/价格/数量等
- 成交 Tab: 支持按来源筛选,显示买卖双方/价格/数量/销毁量/手续费等
- 侧边栏添加「兑换记录」菜单项

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 07:38:46 -08:00
hailin a55201b3b3 feat(referral+mobile): 功能6 — App 团队预种数量展示
纯新增实现,不修改任何现有业务逻辑,对现有系统零风险。

## 后端 — referral-service

新建 TeamPrePlantingController(JWT 认证),2 个公开端点:

1. GET /referral/me/team-pre-planting
   - 返回当前用户的个人预种份数、团队预种总量
   - 返回直推成员列表及每人的预种份数
   - 从 TeamStatistics 表读取(CDC 事件维护的数据)

2. GET /referral/me/team-pre-planting/members?limit=20&offset=0
   - 分页返回全部团队成员的预种明细(仅有预种份数 > 0 的成员)
   - 使用 ancestor_path 数组查询所有下级用户
   - JOIN team_statistics 获取每人的 selfPrePlantingPortions

Kong 网关无需修改(/api/v1/referral/* 已覆盖)。

## 前端 — Flutter mobile-app

新建 TeamPrePlantingPage 页面:
- 顶部统计卡片:个人预种 + 团队预种总量
- 直推预种明细列表(所有用户可见)
- 全部团队成员预种明细(仅市/省公司管理者可见,分页加载更多)
- 普通用户看到锁定提示"仅市公司/省公司管理者可查看"

入口:个人中心页预种按钮行新增绿色「团队预种」按钮。

## 文件清单

新建文件:
- backend/.../controllers/team-pre-planting.controller.ts(核心后端控制器)
- frontend/.../pages/team_pre_planting_page.dart(Flutter 团队预种页面)

微量修改(仅追加新行):
- controllers/index.ts: +1 行 export
- api.module.ts: +2 行 import/注册
- api_endpoints.dart: +2 行端点常量
- referral_service.dart: +4 模型类 +2 API 方法
- route_paths.dart, route_names.dart: +1 行路由定义
- app_router.dart: +1 import +1 GoRoute
- profile_page.dart: 预种按钮行追加第三个按钮

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 07:15:17 -08:00
hailin d3969710be fix(wallet): 系统账户划转前自动结算 settleableUsdt,解决全额划转余额不足
## 问题
省/市区域和团队账户收到的奖励进入 settleableUsdt,
但划转验证只检查 usdtAvailable,导致全额划转报"余额不足"。

## 修复
在 requestSystemWithdrawal 事务中,划转前自动执行结算:
1. 检测账户是否为区域/团队账户(6/7/8/9开头)
2. 若 settleableUsdt > 0,自动结算到 usdtAvailable
3. 记录 REWARD_SETTLED 流水明细(含 trigger=SYSTEM_WITHDRAWAL_AUTO_SETTLE)
4. 结算和划转在同一事务中,保证原子性

## 审计流水
每次自动结算会产生一条独立的 REWARD_SETTLED 流水:
- memo: "系统账户自动结算: 待结算 X 绿积分转入可用余额(划转前自动执行)"
- payloadJson: { settledAmount, previousAvailable, newAvailable, trigger }

## 其他
- 划转白名单扩展支持 6xxx(市团队)、7xxx(省团队)
- getSystemAccountName 添加省团队/市团队名称映射

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 06:30:34 -08:00
hailin 5fad40cec1 fix(mining-app): 修复记录页面时间显示为UTC而非北京时间的问题
所有记录页面的 DateFormat().format() 缺少 .toLocal() 调用,
导致后端返回的 UTC 时间直接显示,与北京时间相差8小时。

修复范围(5个页面,10处):
- mining_records_page: record.createdAt
- batch_mining_records_page: record.createdAt
- contribution_records_page: record.effectiveDate, record.expireDate
- planting_records_page: summary.firstPlantingAt, summary.lastPlantingAt,
  record.adoptionDate, record.createdAt
- trading_records_page: order.createdAt, trade.createdAt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 06:12:16 -08:00
hailin 6e3a898801 fix(admin-web): 系统账户非标准区域代码显示为"测试省/测试市"
调试期间产生的短位数区域代码(如 44、4401)无法匹配标准
6位行政区划码,之前直接显示原始数字。现改为:
- 2位数字 → 测试省 (XX)
- 4位数字 → 测试市 (XXXX)
- 其他未识别纯数字 → 测试账户 (XXX)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 05:56:41 -08:00
hailin 1bfeece109 fix(admin-web): 仪表板预种统计改为方块卡片样式
- 预种待合并/已合并从深色条形卡片改为与认种统计一致的 StatCard
- 统计网格从 4 列调整为 3 列(3+3 布局更工整)
- 骨架屏占位从 4 个调整为 6 个

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 05:24:00 -08:00
hailin 19753a8639 feat(admin-web): 仪表板添加预种待合并/已合并统计卡片
在网络因子/加速因子下方新增一行:
- 预种待合并: 显示全网 pendingMergePortions(份)
- 预种已合并: 显示全网 totalTreesMerged(棵)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 05:19:03 -08:00
hailin 7fff665d9b fix(admin-web): PrePlantingStats 类型添加 pendingMergePortions 字段
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 05:04:57 -08:00
hailin 17df9b6df1 feat(admin): 预种管理页添加"待合并总量"统计项
- planting-service stats 端点新增 pendingMergePortions 字段
  从 PrePlantingPosition 表聚合 SUM(available_portions)
- admin-web 统计卡片区新增"待合并总量(份)"显示
- 网格布局从 4 列调整为 5 列

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 04:17:38 -08:00
hailin b3f3349190 fix(admin-web): 预种管理页更新价格与单位
- 每份价格: 3566 USDT → 1887 绿积分(匹配 10份合1树 方案)
- 金额单位: USDT → 绿积分(状态栏、统计卡片、订单表头)
- 协议 placeholder: 1/5份额 8% → 1/10份额 4%

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 04:07:28 -08:00
hailin 24fe10ee36 fix(authorization): 修复 queryAuthorizations 返回类型缺少 officePhotoUrls
application service 的 queryAuthorizations 方法返回类型声明中
缺少 officePhotoUrls 字段,导致 TypeScript 编译报错。
实际数据映射已正确包含该字段,只是类型注解遗漏。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 03:24:47 -08:00
hailin ac15d6682a feat(admin): 用户预种数量展示 & 授权申请照片查看
## 功能7:用户管理展示预种数量

### 需求
在用户管理(列表+详情页)展示个人及团队预种份数,
并支持跳转预种管理页查看团队中哪些ID购买了预种。

### 后端变更

referral-service — 新增内部 API:
  - GET  /internal/referral/pre-planting-stats/:accountSequence
  - POST /internal/referral/pre-planting-stats/batch
  从 TeamStatistics 表查询 selfPrePlantingPortions/teamPrePlantingPortions

admin-service:
  - 新建 ReferralProxyService (src/referral/) 代理 referral-service 内部 API
  - UserFullDetailDto 新增 selfPrePlantingPortions, teamPrePlantingPortions
  - user-detail.controller 并行调用获取预种统计
  - user.controller 批量获取用户列表预种统计
  - UserListItemDto 新增 selfPrePlantingPortions, teamPrePlantingPortions
  - pre-planting-config.controller 新增 teamOf 查询参数
    → 先从 referralQueryView 获取团队成员列表
    → 将 accountSequences 传递给 planting-service 过滤

planting-service:
  - internal-pre-planting.controller 的 admin orders/positions 端点
    新增 accountSequences 查询参数,支持按用户列表过滤

### 前端变更

admin-web:
  - 用户列表页表格新增"个人预种(份)"、"团队预种(份)"两列
  - 用户详情页新增预种统计卡片(个人/团队预种份数)
  - 团队预种数字可点击,跳转 /pre-planting?teamOf={accountSequence}
  - 预种管理页支持 teamOf URL 参数,团队过滤模式下显示提示条+返回链接

---

## 功能8:授权管理查看申请照片

### 需求
管理后台无法查看社区/市公司/省公司申请时提供的办公室照片。
原因:authorization-service 收到 officePhotoUrls 后只验证未持久化。
照片文件本身已存储在 MinIO,只需持久化 URL 到数据库。

### 后端变更

authorization-service:
  - schema.prisma: AuthorizationRole 新增 officePhotoUrls String[] 字段
  - authorization-role.aggregate: props/字段/getter/构造/toPersistence/所有工厂方法
  - repository: save() create/update 块 + toDomain() 映射
  - application service: 3个 self-apply 方法传递 officePhotoUrls 到 aggregate
  - admin controller: queryAuthorizations 响应包含 officePhotoUrls

admin-service:
  - schema.prisma: AuthorizationRoleQueryView 新增 officePhotoUrls 字段
  - user-detail-query.repository 接口 + impl: 包含 officePhotoUrls
  - user-detail.dto: AuthorizationRoleDto 新增 officePhotoUrls

### 前端变更

admin-web:
  - 授权管理页表格新增"申请照片"列,点击弹出照片网格 Modal
  - 照片支持点击放大(全屏 lightbox 覆盖层)
  - 用户详情页授权信息 Tab 同样支持查看照片
  - authorization.types.ts / userDetail.types.ts 类型更新

---

## 部署注意事项
1. authorization-service 需运行: npx prisma migrate dev --name add-office-photo-urls
2. admin-service 需运行: npx prisma migrate dev --name add-auth-role-office-photos
3. 需部署: authorization-service, admin-service, referral-service, planting-service, admin-web

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 03:09:17 -08:00
hailin d5d61f4f68 fix(transfer): 修复转让记录 API 路径与参数不匹配
- API 路径: /transfers/my → /transfers(后端无 /my 子路由)
- 分页参数: page/pageSize → limit/offset(匹配后端 DTO)
- 转让记录页显示具体错误信息便于调试

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 23:35:09 -08:00
hailin cb9953047f feat(gateway): 添加 transfer-service Kong 路由配置
在 kong.yml 和 kong-standalone.yml 中注册 transfer-service (端口 3013):
- /api/v1/transfers → transfer-api(用户端转让操作)
- /api/v1/admin/transfers → transfer-admin-api(管理端)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:10:09 -08:00
hailin 0cd0bd5694 fix(transfer): Outbox 配置值 Number() 转型,修复 Prisma take 参数类型错误
环境变量读取的值始终是 string,ConfigService.get<number> 不会自动转型,
导致 Prisma findMany({ take: "100" }) 报 String 类型错误。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:55:51 -08:00
hailin 5110915aa8 feat(transfer): 添加 transfer-service 初始数据库 migration
基于 schema.prisma 创建初始 migration SQL,包含:
- transfer_orders: 转让订单表(Saga 聚合根)
- transfer_status_logs: 状态变更审计日志
- outbox_events: Outbox Pattern 事件表
- processed_events: 幂等性保证表

容器启动时 Dockerfile 会自动执行 prisma migrate deploy 建表。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:16:28 -08:00
hailin 6efa74aded refactor(transfer): 移除发起转让页的售价字段 — 线下柜台签合同交易
APP 端树转让仅完成系统层面的所有权变更 + 算力调整(撤回旧用户算力、
新增至新用户及其团队),价格由用户线下到柜台签署合同确定。

变更详情:
- transfer_initiate_page.dart:
  · 移除 _priceController、_feeRate、费用相关 getter
  · 移除"每棵售价"输入框和费用计算卡片 UI
  · 移除 _feeRow 辅助方法
  · 更新说明文案:强调线下柜台签合同
  · 更新确认弹窗:仅显示买方账号和转让棵数
  · API 调用改用 treeCount 替代 pricePerTree
- transfer_service.dart:
  · createTransfer() 参数从 pricePerTree(double) 改为 treeCount(int)
  · 请求体字段同步调整

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:06:31 -08:00
hailin 0e34896d0b refactor(mobile-app): 树转让入口从"我"页面迁移至"实名认证"页面
将"发起转让"和"转让记录"按钮从 ProfilePage 顶部移除,
归类为"树服务"放入 KycEntryPage(实名认证入口页),
位于"其他操作 > 验证/更换手机号"的下方。

变更详情:
- profile_page.dart:
  · 移除 _buildTransferButtons() 方法及其调用
  · 移除 _goToTransferInitiate() / _goToTransferList() 导航方法
- kyc_entry_page.dart:
  · 在"验证/更换手机号"下方新增"树服务"分类标题
  · 新增"发起转让"操作卡片 → /transfer/initiate
  · 新增"转让记录"操作卡片 → /transfer/list
  · 复用页面已有的 _buildActionCard 样式,保持 UI 一致

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:47:39 -08:00
hailin b3984c861c feat(pre-planting): 预种方案调整 — 18870 USDT/棵,10份合1树
=== 方案变更 ===
- 全树基础价: 17830 → 18870 USDT
- 每份价格:   3566 → 1887 USDT
- 合并阈值:   5 份 → 10 份

=== 后端改动 (planting-service) ===
1. pre-planting-right-amounts.ts:
   - PRE_PLANTING_PRICE_PER_PORTION: 3566 → 1887
   - PRE_PLANTING_PORTIONS_PER_TREE: 5 → 10
   - 10类权益金额按 floor(整棵树/10) 重算,余数归 HQ_BASE_FEE(319)
2. pre-planting-merge.aggregate.ts:
   - 合并校验从硬编码 5 改为引用 PRE_PLANTING_PORTIONS_PER_TREE 常量
3. purchase-pre-planting.dto.ts:
   - portionCount @Max(5) → @Max(10)
4. pre-planting-application.service.ts:
   - 加价补贴计算 /5 → /PRE_PLANTING_PORTIONS_PER_TREE
   - 错误文案引用常量,消除硬编码

=== 前端改动 (mobile-app) ===
1. pre_planting_purchase_page.dart: 默认价格、份数、协议文本(1/10、4%)
2. pre_planting_position_page.dart: _portionsPerTree 5→10
3. pre_planting_merge_detail_page.dart: 总价值计算和单份显示金额
4. tree_pricing_service.dart: fallback 默认值
5. pre_planting_service.dart: JSON 解析 fallback 默认值
6. 各文件注释同步更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:32:32 -08:00