hailin
7c95d1d425
fix(admin-service): admin config 端点也返回 agreementText
...
planting-service 调用的是 admin 控制器的 getConfig()(非 public 控制器),
因为 public 控制器有双重 api/v1 前缀。确保 admin getConfig 也包含协议文本。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:01:03 -08:00
hailin
5131728835
fix(planting-service): 添加 ADMIN_SERVICE_URL 环境变量
...
planting-service 的 PrePlantingPublicController 需要调用 admin-service
获取预种配置(含协议文本),但 docker-compose 中缺少 ADMIN_SERVICE_URL
环境变量,导致默认使用 localhost:3010 连接失败。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 21:58:42 -08:00
hailin
c0ac63d40a
feat(pre-planting): 重命名预种持仓→预种明细 + 购买协议弹窗
...
- mobile-app: "预种持仓"按钮和页面标题改为"预种明细"
- admin-service: 新增预种协议文本 API (GET/PUT agreement),存储于 system_configs 表
- admin-service: 公开 config API 响应增加 agreementText 字段
- planting-service: 新建 PrePlantingPublicController (无需 JWT),暴露 GET /pre-planting/config
- admin-web: 预种管理页面新增协议文本编辑器(textarea + 保存按钮)
- mobile-app: 购买流程增加协议弹窗,用户需勾选同意后才能继续
- mobile-app: 协议文本优先使用后台配置,未配置时使用默认文本
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 21:14:24 -08:00
hailin
92054e776e
fix(contribution): 复制预种Prisma生成客户端到dist目录修复运行时MODULE_NOT_FOUND
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:35:26 -08:00
hailin
30a2f739cb
fix(contribution): Dockerfile添加预种Prisma Client生成和migration
...
- builder和runner阶段均添加 prisma generate --schema=prisma/pre-planting/schema.prisma
- start.sh添加预种migration部署步骤
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:33:04 -08:00
hailin
8d7fd68509
fix(admin): 预种开关DTO添加class-validator装饰器,修复400错误
...
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:26:42 -08:00
hailin
37a5610d74
feat(admin): 实现预种管理页面完整API端点
...
planting-service: InternalPrePlantingController 新增4个管理员查询端点
- GET /internal/pre-planting/admin/orders (分页订单列表)
- GET /internal/pre-planting/admin/positions (分页持仓列表)
- GET /internal/pre-planting/admin/merges (分页合并记录)
- GET /internal/pre-planting/admin/stats (统计汇总)
admin-service: HTTP代理层新增5个端点
- PUT config/toggle (开关切换)
- GET orders/positions/merges/stats (代理转发到planting-service)
- 新建 PrePlantingProxyService (复用ContractService的axios代理模式)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:21:31 -08:00
hailin
63a169abb0
fix(cdc): deploy-mining.sh 添加预种CDC connector管理
...
更新 CDC_POSTGRES_CONNECTORS 数组和所有 case 映射(resnapshot、
full-reset topic清理、Step9重注册),确保2.0部署脚本能正确
管理预种CDC connector的生命周期。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:58:49 -08:00
hailin
f270b7cc27
fix(cdc): 添加3171预种计划Debezium CDC connector配置
...
预种CDC消费端(contribution-service)代码已就绪,但缺少Debezium
connector配置,导致pre_planting_orders和pre_planting_positions
表变更无法捕获到Kafka,算力无法同步。
新增:
- pre-planting-connector.json: 监听rwa_planting库的pre_planting_*表
独立slot/publication/topic前缀(cdc.pre-planting)
- register-connectors.sh: 注册pre-planting-postgres-connector
- deploy.sh: infra-status显示所有1.0 connector状态
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:56:42 -08:00
hailin
843f817976
fix(kong): 添加3171预种计划API网关路由
...
预种控制器 @Controller('pre-planting') 的路由 /api/v1/pre-planting
未在 Kong 网关中配置,导致所有预种API请求返回 "no Route matched"。
新增 pre-planting-api 路由指向 planting-service (192.168.1.111:3003)。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:24:49 -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
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
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
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
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
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
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
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
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
cf07eb03be
feat(snapshot): 数据快照备份服务全量实现(纯新增,零侵入)
...
一套代码两处部署的在线备份编排服务,为 1.0 认种分配系统和 2.0 算力挖矿系统
分别提供 PostgreSQL / Redis / Kafka / ZooKeeper / MinIO / Uploads 的在线备份能力。
管理员在 admin-web / mining-admin-web 中选择备份目标和存储方式,点击备份后系统
串行执行各组件备份,通过 Socket.IO WebSocket 实时推送进度到前端。
## 后端 snapshot-service(NestJS 10 + Prisma 5 + SQLite)
架构: DDD 四层(api / application / domain / infrastructure)
- api 层:
· SnapshotController — REST API(创建/查询/删除/下载)含 Range/206 断点续传
· SnapshotGateway — Socket.IO WebSocket 实时推送 5 类事件
· HealthController — 健康检查
· CreateSnapshotDto — class-validator 验证
· toSnapshotResponse — BigInt→string 序列化
- application 层:
· SnapshotOrchestratorService — 核心编排引擎
- startSnapshot() 异步启动,不阻塞 HTTP
- 按 PG→Redis→Kafka→ZK→MinIO→Uploads 顺序串行执行
- 单目标失败不中断整体任务
- MinIO 模式: 备份完上传到 MinIO 后删除本地临时文件
- LOCAL 模式: 保留在服务器临时目录供下载
- @Cron(EVERY_HOUR) 自动清理过期本地备份(默认 72h)
- runningTaskId 防止并发执行
- domain 层:
· BackupTarget 枚举(6 种目标)+ BACKUP_TARGET_ORDER 执行顺序
· SnapshotStatus 枚举(PENDING/RUNNING/COMPLETED/FAILED)
· StorageType 枚举(MINIO/LOCAL)
· BackupHandler 接口 + BACKUP_HANDLER_TOKEN
- infrastructure 层:
· 6 个备份 Handler(均实现 BackupHandler 接口):
- PostgresBackupHandler: pg_basebackup 通过网络流式备份,解析 stderr 进度
- RedisBackupHandler: BGSAVE + LASTSAVE 轮询 + 打包 dump.rdb/AOF
- KafkaBackupHandler: archiver 打包数据卷,按字节计算进度
- ZookeeperBackupHandler: archiver 打包 data/ + log/
- MinioBackupHandler: SDK 列举并下载所有桶(排除备份桶)后打包
- UploadsBackupHandler: archiver 打包上传文件目录
· 2 个存储适配器:
- MinioStorageAdapter: fPutObject 上传 / removeObjects 批量删除
- LocalStorageAdapter: 本地临时目录管理 + 过期清理
· PrismaService (SQLite) + SnapshotRepository (完整 CRUD)
· BACKUP_HANDLER_TOKEN 工厂: 根据 AVAILABLE_TARGETS 环境变量过滤可用 handler
- Prisma Schema (SQLite):
· SnapshotTask: 主表,targets 存 JSON 字符串,totalSize 用 BigInt
· SnapshotDetail: 明细表,每个目标一行,@@index([taskId])
· onDelete: Cascade 级联删除
- Dockerfile: 多阶段构建,生产镜像安装 postgresql-client + mc (MinIO CLI)
SQLite 使用 prisma db push 而非 migrate deploy
- 部署端口: 1.0 系统 = 3099,2.0 系统 = 3199
## Docker Compose overlay(纯新增,不修改现有 docker-compose)
- docker-compose.snapshot.yml (1.0):
· 挂载 redis_data/kafka_data/zookeeper_data/zookeeper_log/admin_uploads_data 只读卷
· AVAILABLE_TARGETS=POSTGRES,REDIS,KAFKA,ZOOKEEPER,MINIO,UPLOADS
· 依赖 postgres + redis 健康检查
- docker-compose.2.0-snapshot.yml (2.0 standalone):
· 挂载 redis_2_data/mining-admin-uploads/trading-uploads 只读卷
· AVAILABLE_TARGETS=POSTGRES,REDIS,UPLOADS
· 依赖 postgres-2 + redis-2 健康检查
## 前端 admin-web(Next.js 15 + SCSS)
- 新增 /snapshots 页面: 创建备份表单 + 实时进度条 + 历史列表 + 下载/删除
- 新增 useSnapshotWebSocket hook: Socket.IO 连接 + 5 类事件监听
- 新增 snapshot.api.ts: 独立 fetch(不走通用 apiClient,snapshot 服务独立端口)
- 新增 snapshot.types.ts: 共享类型定义
- 新增 page.module.scss: 表单/进度条/表格样式
- 修改 Sidebar.tsx: 添加「数据快照」菜单项
- package.json: 添加 socket.io-client 依赖
## 前端 mining-admin-web(Next.js 14 + Tailwind CSS)
- 新增 /snapshots 页面: 同 admin-web 功能,Tailwind CSS 风格
- 新增 useSnapshotWebSocket hook
- 新增 snapshot.api.ts + snapshot.types.ts
- 修改 sidebar.tsx: 添加「数据快照」菜单项 + HardDrive 图标
- package.json: 添加 socket.io-client 依赖
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 21:53:09 -08:00
hailin
a11e4d0261
fix(pre-planting): 修复 5 个预种模块 Bug + 补全 3 服务 migration
...
Bug #1 (HIGH): allocateFunds 从 Prisma 事务内移到事务外
- pre-planting-reward.service.ts: distributeRewards 拆为
prepareAndPersistRewards(事务内持久化)+ executeAllocations(事务后转账)
- pre-planting-application.service.ts: 事务后调用 executeAllocations
Bug #2 (HIGH): signContract 后触发 hasPlanted
- 签约事务成功后发布 PlantingOrderPaid 到 planting-events topic
- wallet-service 消费后执行 markUserAsPlanted + settleUserPendingRewards
- event-publisher.service.ts: 新增 publishRawToPlantingEvents 方法
Bug #3 (MEDIUM): PENDING 推荐奖励改为全部 SETTLED
- 与现有认种行为对齐,推荐奖励立即发放
Bug #4 (HIGH): 补全 3 个服务的数据库迁移文件
- planting-service: 4 张预种表(orders/positions/merges/reward_entries)
- admin-service: 1 张配置表(pre_planting_configs)+ 默认数据
- contribution-service: 4 张 CDC 追踪表(synced_orders/positions/freeze_states/processed_cdc_events)
Bug #5 (LOW): 合并循环 if→while,支持一次购买多份触发多次合并
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:09:14 -08:00
hailin
b3a3652f21
feat(transfer): 树转让功能全量实现(纯新增,零侵入)
...
实现已认种果树所有权在用户间转让的完整功能。采用方案一:
独立 transfer-service 微服务 + Saga 编排器模式。
=== 架构设计 ===
- Saga 编排器 8 步正向流程:卖方确认 → 冻结资金 → 锁定树 →
变更所有权 → 调整算力 → 更新统计 → 结算资金 → 完成
- 补偿回滚:任一步骤失败自动反向补偿(解冻资金 → 解锁树)
- 13 种状态:PENDING → SELLER_CONFIRMED → PAYMENT_FROZEN →
TREES_LOCKED → OWNERSHIP_TRANSFERRED → CONTRIBUTION_ADJUSTED →
STATS_UPDATED → PAYMENT_SETTLED → COMPLETED / CANCELLED /
FAILED / ROLLING_BACK / ROLLED_BACK
=== Phase 1-2: transfer-service(独立微服务) ===
新建文件:
- Prisma Schema:transfer_orders + transfer_status_logs + outbox_events
- Domain:TransferOrder 聚合根 + TransferFeeService(5% 手续费)
- Application:TransferApplicationService + SagaOrchestratorService
- Infrastructure:Kafka 事件消费/生产 + Outbox Pattern
- API:TransferController(用户端)+ AdminTransferController(管理端)
- External Clients:wallet/planting/identity-service HTTP 客户端
- Docker + 环境配置
=== Phase 3: 现有微服务扩展(纯追加) ===
planting-service:
- Prisma schema 追加 transferLockId 可空字段
- InternalTransferController:锁定/解锁/执行 3 个新端点
- Kafka handlers:transfer-lock/execute/rollback 事件处理
- main.ts 追加 Kafka consumer group 配置
referral-service:
- PlantingTransferredHandler:处理转让后团队统计更新
- TeamStatisticsAggregate 追加 handleTransfer() 方法
- TeamStatisticsRepository 追加 adjustForTransfer() 方法
- ProvinceCityDistribution 追加 transferTrees() 方法
contribution-service:
- TransferOwnershipHandler:处理所有权变更事件
- TransferAdjustmentService:算力调整(879 行核心逻辑)
- Prisma schema 追加 transferOrderId 可空字段
- ContributionAccount 追加 applyTransferAdjustment() 方法
=== Phase 4A: wallet-service(3 个新内部端点) ===
新建文件:
- FreezeForTransferDto / UnfreezeForTransferDto / SettleTransferDto
- FreezeForTransferCommand / UnfreezeForTransferCommand / SettleTransferPaymentCommand
- InternalTransferWalletController(POST freeze/unfreeze/settle-transfer)
修改文件:
- wallet-application.service.ts 追加 3 组方法(+437 行):
freezeForTransfer / unfreezeForTransfer / settleTransferPayment
(乐观锁 + 3 次重试 + Prisma $transaction + 幂等检查)
- 结算操作:单事务内更新 3 个钱包(买方扣减 + 卖方入账 + 手续费归集)
=== Phase 4B: admin-web(转让管理页面) ===
新建文件:
- transferService.ts:API 调用服务 + 完整类型定义
- useTransfers.ts:React Query hooks(list/detail/stats/forceCancel)
- /transfers/page.tsx:列表页(统计卡片 + 搜索筛选 + 分页 + 13 种状态 badge)
- /transfers/[transferOrderNo]/page.tsx:详情页(Saga 时间线 + 状态日志 + 强制取消)
- transfers.module.scss:完整样式
修改文件:
- endpoints.ts 追加 TRANSFERS 端点配置
- Sidebar.tsx 追加「转让管理」菜单项
- hooks/index.ts 追加 useTransfers 导出
=== Phase 4C: mobile-app(转让 UI) ===
新建文件:
- transfer_service.dart:Flutter API 服务 + Model(TransferOrder/Detail/StatusLog)
- transfer_list_page.dart:转让记录列表(全部/转出/转入 Tab + 下拉刷新)
- transfer_detail_page.dart:转让详情(Saga 时间线 + 确认/取消操作)
- transfer_initiate_page.dart:发起转让表单(手续费自动计算)
修改文件:
- injection_container.dart 追加 transferServiceProvider
- route_paths.dart + route_names.dart 追加 3 个路由
- app_router.dart 追加 3 个 GoRoute
- profile_page.dart 追加「发起转让」+「转让记录」按钮行
=== 基础设施 ===
- docker-compose.yml 追加 transfer-service 容器配置
- deploy.sh 追加 transfer-service 部署
- init-databases.sh 追加 transfer_db 数据库初始化
=== 纯新增原则 ===
所有变更均为追加式修改,不修改任何现有业务逻辑:
- 新增 nullable 字段(不影响现有数据)
- 新增 enum 值(不影响现有枚举使用)
- 新增 providers/controllers(不影响现有依赖注入)
- 新增页面/路由(不影响现有页面行为)
回滚方式:删除 transfer-service 目录 + 移除各服务中带 [2026-02-19] 标记的代码
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 03:44:02 -08:00
hailin
e1cd8ed7f2
feat(pre-planting): 3171 预种计划 2.0 算力集成(contribution-service CDC 模块)
...
Phase 5:将预种数据集成到 contribution-service 2.0 算力体系。
=== 新增文件(11 个) ===
- prisma/pre-planting/schema.prisma:独立 Prisma schema(4 张追踪表)
- PrePlantingPrismaService + Module:独立 PrismaClient
- PrePlantingCdcConsumerService:独立 CDC 消费者(consumer group: contribution-pre-planting-cdc)
- PrePlantingOrderSyncedHandler:订单 CDC handler + synced_adoptions marker 插入
- PrePlantingPositionSyncedHandler:持仓 CDC handler
- PrePlantingCdcDispatcher:CDC 事件分发器
- PrePlantingContributionService:1/5 算力计算(复用领域计算器)
- PrePlantingFreezeScheduler:每日冻结/解冻调度(凌晨 5 点)
- PrePlantingCdcModule:模块注册
- constants.ts:10B 偏移量、冻结期限等常量
=== 隔离保证 ===
- 独立 Kafka consumer group(contribution-pre-planting-cdc)
- 独立 CDC topics(cdc.pre-planting.public.*)
- 独立 Prisma schema + generated client
- sourceAdoptionId 使用 10,000,000,000 偏移避免 ID 冲突
- synced_adoptions marker: contributionDistributed=true + treeCount=0
- 不更新 NetworkAdoptionProgress(预种不推高全网算力系数)
- 现有代码文件零修改(仅 app.module.ts 加 1 行 import)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 05:25:14 -08:00
hailin
010b0392fd
feat(pre-planting): 3171 预种计划 1.0 全量实现(纯新增,零侵入)
...
预种计划(拼种团购):用户以 3171 USDT/份参与认种(1棵树的1/5价格),
累计5份自动合成1棵树,触发合同签署并解除交易/提现限制。
涉及服务(现有代码仅 app.module.ts 加 1 行 import,其余全部为新增文件):
- planting-service: PrePlantingModule(独立聚合根、购买/合并/签约/分配)
- admin-service: 预种开关管理(PrePlantingConfig 表 + API)
- referral-service: PrePlantingStatsModule(消费预种事件更新团队统计)
- authorization-service: PrePlantingGuardModule(未合并不可申请授权)
- wallet-service: PrePlantingGuardModule(未合并不可提现)
新增数据表:pre_planting_orders, pre_planting_positions,
pre_planting_merges, pre_planting_reward_entries, pre_planting_configs
新增 Kafka Topics:pre-planting.portion.purchased, pre-planting.merged,
pre-planting.contract.signed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 05:06:03 -08:00
hailin
875f86c263
fix(reporting): 修复 contract.signed 事件 BigInt(undefined) 错误
...
根因:planting-service 发送的 contract.signed 消息为嵌套结构
{ eventName: 'contract.signed', data: { orderNo, userId, ... } }
但 reporting-service handleContractSigned 按扁平结构解析
message.userId → undefined → BigInt(undefined) → TypeError
导致 ~200 次/10分钟持续报错。
修复:
- 消息类型改为匹配实际嵌套格式 { eventName, data: { ... } }
- 解构 message.data 后再访问各字段
- 添加 userId 防御性检查,避免再次 BigInt 崩溃
- 与 referral-service ContractSigningHandler 消息结构保持一致
影响范围:仅 reporting-service 活动记录,不影响核心业务流程
(referral-service / reward-service 已正确处理嵌套格式)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 07:59:16 -08:00
hailin
2a725af83e
fix: Debezium CDC 全面安全加固 (1.0 + 2.0)
...
问题背景:
- 1.0 生产环境发现 6 个孤儿 replication slot, WAL 积压 8.6GB (已清理)
- 1.0 使用 Debezium 2.4, 存在 DBZ-7316 (WAL 无限积压) bug
- 1.0 和 2.0 均无 max_slot_wal_keep_size 安全阀 (已在线设置 10GB)
- 2.0 outbox connector 使用 pg_logical_emit_message 心跳, 不经 publication
- 2.0 outbox connector RegexRouter regex=".*" 导致 heartbeat 污染消费者
修复内容:
[docker-compose.yml - 1.0 基础设施]
- Debezium: 2.4 → 2.5.4.Final (修复 DBZ-7316)
- PostgreSQL: 添加 max_slot_wal_keep_size=10GB
- Debezium REST API: 端口绑定 127.0.0.1 (防 SSRF 注入)
- PostgreSQL: 端口绑定 127.0.0.1 (防公网直连)
- Kafka Connect: 添加 OFFSET_FLUSH_INTERVAL_MS=10s
[docker-compose.2.0.yml - 2.0 基础设施]
- Debezium: 2.5 → 2.5.4.Final (锁定精确版本)
- PostgreSQL: 添加 max_slot_wal_keep_size=10GB
- Kafka Connect: 添加 OFFSET_FLUSH_INTERVAL_MS=10s
[1.0 Connector 配置 - identity/authorization]
- 添加 heartbeat.action.query (INSERT INTO debezium_heartbeat TABLE 方式)
- 之前只有 heartbeat.interval.ms 无 action.query, 心跳不生效
[2.0 Outbox Connector 配置 - 5个全部更新]
- heartbeat: pg_logical_emit_message → INSERT INTO debezium_heartbeat TABLE 方式
(TABLE 方式经过 publication → Debezium 消费 → 推进 confirmed_flush_lsn)
- RegexRouter: regex ".*" → ".*outbox_events" (只路由 outbox 事件, heartbeat 走默认 topic)
- table.include.list: 添加 debezium_heartbeat (确保心跳变更生成 Kafka 消息)
- publication.autocreate.mode: filtered → disabled (使用预创建的 publication)
- auth/contribution: 添加 signal channel 配置 (支持增量快照数据重放)
经验总结:
1. pg_logical_emit_message 写 WAL 但不经 publication, 无法推进 confirmed_flush_lsn
2. RegexRouter regex=".*" 把所有变更(含 heartbeat)路由到 outbox topic, 污染消费者
3. 删除 Kafka Connect connector 不会自动清理 PostgreSQL replication slot
4. max_slot_wal_keep_size 是 sighup 级参数, 可在线 ALTER SYSTEM + pg_reload_conf
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 07:32:13 -08:00
hailin
54eb472faa
fix(debezium): 添加 heartbeat.action.query 防止 WAL 无限积压
...
问题根因:
- 5个 Debezium outbox connector 配置了 heartbeat.interval.ms=10000
但缺少 heartbeat.action.query,导致低写入频率的数据库
(auth/contribution/mining-wallet) 无法向 PostgreSQL 回传
confirmed_flush_lsn 确认位点
- PostgreSQL 无法回收旧 WAL,磁盘占用持续增长至 293GB
修复方案 (参考 Debezium 创始人 Gunnar Morling 推荐):
- 使用 pg_logical_emit_message() 代替心跳表方案
- 每10秒向 WAL 写入逻辑解码消息,绕过 publication 过滤
- 无需建表、无需改 table.include.list、无需改 publication
- 不产生额外 Kafka 消息,对消费端零影响
- 要求 PostgreSQL >= 14 (当前 16.11)
影响范围: 全部5个 outbox connector 配置文件
操作方式: PUT /connectors/{name}/config 热更新,触发 task 重启
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 02:44:38 -08:00
hailin
ab9212cefa
security: 基础设施端口绑定 127.0.0.1,封堵公网暴露
...
PostgreSQL(5432)、Redis(6379)、Debezium REST API(8084) 此前绑定
0.0.0.0,直接暴露在公网。安全审查发现 Debezium 已被注入 3 个恶意
connector(SSRF 攻击尝试读取 /etc/passwd),恶意 connector 已清除。
修改内容:
- PostgreSQL: 0.0.0.0:5432 → 127.0.0.1:5432
- Redis: 0.0.0.0:6379 → 127.0.0.1:6379
- Debezium: 0.0.0.0:8084 → 127.0.0.1:8084
deploy-mining.sh 通过 docker exec 和 localhost 访问,不受影响。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 19:23:42 -08:00
hailin
bc3d800936
feat(mining-app): 贡献值730天失效倒计时功能
...
将贡献值页面的"贡献值失效倒计时"从硬编码静态文字改为基于用户
首次挖矿时间的真实730天倒计时。纯新增方式实现,不影响现有功能。
后端 (mining-service):
- get-mining-account.query.ts: MiningAccountDto 新增 firstMiningDate
字段,在 Promise.all 中并行查询用户最早的 miningRecord,利用
@@unique([accountSequence, miningMinute]) 索引高效查询
前端实体/模型:
- share_account.dart: 新增 DateTime? firstMiningDate(可空,向后兼容)
- share_account_model.dart: fromJson/toJson 解析和序列化 firstMiningDate
前端 UI (contribution_page.dart):
- watch shareAccountProvider 获取首次挖矿时间
- 计算已过天数和剩余天数(730 - 已过天数)
- 进度条显示实际已用时间占比
- 显示具体失效日期和剩余天数
- 无挖矿记录 → 显示"暂无挖矿记录"
- 已过期 → 显示"贡献值已失效"
- 剩余 ≤30 天 → 进度条和文字变红色警告
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:30:01 -08:00
hailin
80e3fdb7e0
fix(admin-service): 修复合同服务调用 planting-service 端口和路径错误
...
- 端口从 3002 改为 3003(与 planting-service 实际端口一致)
- API 路径添加 /api/v1 前缀(planting-service 有全局前缀)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 23:20:26 -08:00
hailin
6845ad4a0f
fix(contracts): 移除控制器路径中的重复 v1 前缀
...
全局前缀已是 api/v1,控制器路径应为 admin/contracts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 23:12:34 -08:00
hailin
0e058caa28
fix(contracts): Prisma JSON 字段使用 undefined 替代 null
...
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 23:02:02 -08:00
hailin
05e2c29f37
chore(admin-service): 更新 package-lock.json 添加 archiver/axios 依赖
...
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:58:18 -08:00
hailin
1bb12783db
fix(contracts): 修复合同状态枚举 EXPIRED -> UNSIGNED_TIMEOUT
...
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:55:06 -08:00
hailin
86461a052d
feat(contracts): 合同管理功能 - 查询/下载/批量打包
...
新增功能:
- 合同列表查询(支持省市、状态、时间筛选)
- 单个合同 PDF 下载(支持断点续传)
- 批量下载 ZIP 打包(异步任务处理)
- 增量下载(基于上次下载时间)
- 用户详情页合同 Tab
后端:
- planting-service: 内部合同查询 API
- admin-service: 合同管理控制器、服务、批量下载 Job
- 新增 contract_batch_download_tasks 表
前端:
- 新增独立合同管理页面 /contracts
- 用户详情页新增合同信息 Tab
- 侧边栏新增合同管理入口
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:50:17 -08:00
hailin
a1aba14ccf
feat(trade-password): 实现卖出交易的支付密码验证功能
...
## 后端改动
### auth-service
- user.aggregate.ts: 添加支付密码相关方法 (setTradePassword, verifyTradePassword, hasTradePassword)
- trade-password.service.ts: 新建支付密码业务逻辑服务
- trade-password.controller.ts: 新建支付密码 REST API (status/set/change/verify)
- user.repository.ts: 添加 tradePasswordHash 字段的持久化
- schema.prisma: 添加 trade_password_hash 字段
- migration 0003: 添加支付密码字段迁移
### trading-service
- audit-ledger.service.ts: 新建审计分类账服务 (Append-Only设计,仅INSERT)
- schema.prisma: 添加 AuditLedger 模型和 AuditActionType 枚举
- migration 0008: 添加审计分类账表迁移
## 前端改动 (mining-app)
### 新增页面/组件
- trade_password_page.dart: 支付密码设置/修改页面 (6位数字)
- trade_password_dialog.dart: 交易时的支付密码验证弹窗
### 功能集成
- trading_page.dart: 卖出时检查支付密码
- 未设置: 提示用户跳转设置页面
- 已设置: 弹出验证弹窗,验证通过后才能卖出
- profile_page.dart: 账户设置增加"支付密码"入口
- user_providers.dart: 添加支付密码相关 Provider
- auth_remote_datasource.dart: 添加支付密码 API 调用
- api_endpoints.dart: 添加支付密码 API 端点
- routes.dart/app_router.dart: 添加支付密码页面路由
## 安全设计
- 支付密码独立于登录密码 (6位纯数字)
- 审计分类账采用链式哈希保证完整性
- 所有敏感操作记录不可变审计日志
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:12:39 -08:00
hailin
d29454fc74
feat(admin-web): 省团队收益汇总明细增加来源用户列并修复账户名称显示
...
问题描述:
1. 省团队收益汇总的详细明细中缺少收益来源用户信息
2. 账户类型显示为数字(如7440000)而不是省名称
修改内容:
1. 后端 reward-service (reward-application.service.ts)
- getRewardEntriesByType 方法返回值新增 sourceAccountSequence 字段
- 该字段表示触发此收益的用户账户序列号(来自认种用户)
2. 后端 reporting-service (reward-service.client.ts)
- RewardEntryDTO 接口新增 sourceAccountSequence 字段
3. 前端 admin-web
- system-account.types.ts: RewardEntryDTO 新增 sourceAccountSequence 字段
- system-account.types.ts: getAccountDisplayName 函数支持7开头的省团队账户
示例:7440000 → "广东省团队 (7440000)"
- SystemAccountsTab.tsx: 详细明细表格新增"来源用户"列
效果:
- 省团队收益明细现在显示:时间、账户(省名称)、来源用户、订单号、金额、状态
- 账户列显示格式:"{省名}省团队 ({账户序列号})"
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:35:22 -08:00
hailin
207b522754
feat(customer-service): 客服联系方式从硬编码改为后台可配置
...
将 mobile-app "联系客服" 弹窗的微信/QQ联系方式从硬编码改为
admin-web 后台动态配置,支持任意数量的联系方式管理。
## Backend (admin-service)
- 新增 Prisma 模型: ContactType 枚举(WECHAT/QQ) + CustomerServiceContact
- 新增迁移 SQL: 建表 + 2条索引 + 4条种子数据(保留现有硬编码联系方式)
- 新增双 Controller (参考 app-asset.controller.ts 模式):
- AdminCustomerServiceContactController (admin/customer-service-contacts)
GET 列表 / POST 新增 / PUT 更新 / DELETE 删除
- PublicCustomerServiceContactController (customer-service-contacts)
GET 仅返回 isEnabled=true,按 sortOrder 排序
- 注意: 公开 Controller 用 @Controller('customer-service-contacts')
避免与全局前缀 api/v1 双重叠加
## Kong 网关
- 新增路由 admin-customer-service-contacts-public
路径 /api/v1/customer-service-contacts → admin-service:3010
- Admin 端点由已有 admin-api 路由 (/api/v1/admin) 覆盖
## Admin-web
- endpoints.ts: 新增 CUSTOMER_SERVICE_CONTACTS 端点组
- customerServiceContactService.ts: CRUD 服务 (list/create/update/delete)
- settings/page.tsx: 新增"客服联系方式管理"区块
表格展示(排序/类型/标签/联系方式/启停/操作) + 内联新增/编辑表单
- settings.module.scss: contactTable / contactForm / contactFormFields 样式
## Flutter Mobile-app
- storage_keys.dart: 新增 cachedCustomerServiceContacts 缓存 key
- customer_service_contact_service.dart: API + 缓存服务
(内存5分钟TTL + SharedPreferences持久化 + 后台静默刷新)
- injection_container.dart: 注册 customerServiceContactServiceProvider
- profile_page.dart: _showCustomerServiceDialog() 从硬编码改为
动态 API 加载,contacts 为空时显示"暂无客服联系方式"占位符
## 文件清单 (4 新建 + 9 修改)
新建:
- backend/.../migrations/20260205100000_add_customer_service_contacts/migration.sql
- backend/.../controllers/customer-service-contact.controller.ts
- frontend/admin-web/src/services/customerServiceContactService.ts
- frontend/mobile-app/lib/core/services/customer_service_contact_service.dart
修改:
- backend/.../prisma/schema.prisma
- backend/.../src/app.module.ts
- backend/api-gateway/kong.yml
- frontend/admin-web/src/infrastructure/api/endpoints.ts
- frontend/admin-web/src/app/(dashboard)/settings/page.tsx
- frontend/admin-web/src/app/(dashboard)/settings/settings.module.scss
- frontend/mobile-app/lib/core/storage/storage_keys.dart
- frontend/mobile-app/lib/core/di/injection_container.dart
- frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 05:00:25 -08:00
hailin
34ba209e44
fix(app-assets): 修复公开API路径双重前缀 + Kong网关路由缺失
...
问题:移动端配置的开屏图/引导图无效
根因:
1. PublicAppAssetController 和 PublicSystemConfigController 的
@Controller('api/v1/xxx') 与 NestJS 全局前缀 api/v1 叠加,
导致实际端点为 api/v1/api/v1/xxx(双重前缀)
2. Kong 网关缺少 /api/v1/app-assets 和 /api/v1/system-config 路由
3. Flutter 端使用 /admin-service/api/v1/xxx 路径,不匹配任何 Kong 路由
修复:
- 后端:Controller 路径去掉 api/v1 前缀,由全局前缀统一添加
- Kong:新增 admin-app-assets-public 和 admin-system-config-public 路由
- Flutter:API 路径改为 /app-assets 和 /system-config/display/settings
受影响文件:
- backend/api-gateway/kong.yml (新增2条路由)
- backend/.../app-asset.controller.ts (Controller路径修正)
- backend/.../system-config.controller.ts (Controller路径修正)
- frontend/.../app_asset_service.dart (API路径修正)
- frontend/.../system_config_service.dart (API路径修正)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 04:36:53 -08:00
hailin
b639b5d499
fix(mining-app): 价格显示优化 + 涨跌幅精度再提升
...
- 兑换页顶部价格改用 formatPriceFull 显示完整零(不再用 0.0{n} 缩写)
- K线纵坐标有效数字从7位精简为4位,节省空间更简洁
- 涨跌幅精度再提升:后端 toFixed(8)→toFixed(12),前端 6位→10位小数
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 20:43:48 -08:00
hailin
2e91686a88
fix(mining-app): 优化页面文案与涨跌幅精度
...
- 兑换页顶部隐藏价格后方"积分值"文字,界面更简洁
- 兑换页"较上线首日"涨跌幅精度从2位小数提升至6位,每分钟销毁引起的涨幅尾数可见
- 兑换页"黑洞销毁量"文案改为"已销毁量",更直观易懂
- 贡献值页"积分股池实时余量"文案改为"100亿销毁剩余量"
- 后端 priceChangePercent 精度从 toFixed(2) 提升至 toFixed(8),支持前端高精度显示
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 20:23:59 -08:00
hailin
59acea33fe
feat(trading): K线图增加周/月/年周期
...
- 前端周期选项增加「周」「月」「年」
- 后端 parsePeriodToMinutes 增加 1w/1M/1y 映射
- getPeriodStart 对日历周期按自然边界对齐:
- 1w → 周一 00:00 UTC(ISO 周标准)
- 1M → 月初1日 00:00 UTC
- 1y → 年初1月1日 00:00 UTC
- 固定周期(1m~1d)仍使用 epoch 模运算,不受影响
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 18:42:38 -08:00
hailin
c24f383501
fix(trading): K线Y轴动态缩放 + 价格显示格式统一 + 首日涨幅修复
...
- K线图Y轴改为只基于可见K线计算范围,实现动态缩放(类似TradingView)
- 价格显示统一使用 0.0{n}xxx 格式替代科学计数法,覆盖全局 formatPrice、
K线图、交易记录页、挖矿记录页
- 修复"较上线首日"百分比始终显示 +0.00% 的问题:getFirstSnapshot 过滤
price=0 的早期快照
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 18:36:26 -08:00
hailin
d075853a7f
feat(app-assets): 应用图片管理 — 开屏图/引导图可从 admin-web 配置
...
新增从 admin-web 后台管理开屏图(2张)和引导图(5张+标题/副标题)的完整功能链路。
移动端优先使用后台配置的远程图片,无配置或加载失败时自动回退到本地 asset。
### Backend (admin-service)
- Prisma schema 新增 AppAssetType 枚举 + AppAsset 模型 (type/sortOrder 唯一约束)
- 新增 AdminAppAssetController: 图片上传(multipart)、列表查询、元数据更新、删除
- 新增 PublicAppAssetController: 公开查询接口供移动端消费 (仅返回 isEnabled=true)
- 新增数据库 migration: 20260204100000_add_app_assets
### Admin-web
- endpoints.ts 新增 APP_ASSETS 端点组
- 新增 appAssetService.ts: list/upload/update/delete 方法
- Settings 页新增"应用图片管理"区块: 开屏图 2 卡槽 + 引导图 5 卡槽
- 每个卡槽支持: 图片上传预览、启用/禁用开关、删除、引导图额外支持标题和副标题编辑
### Mobile-app (Flutter)
- 新增 AppAssetService: 3 级缓存策略 (内存 5min TTL → SharedPreferences → 后台静默刷新)
- splash_page.dart: 支持远程开屏图 (CachedNetworkImage),fallback 到本地 asset
- guide_page.dart: 支持远程引导图+标题/副标题覆盖,fallback 到本地 asset
- 替换 2 张开屏图为新版 (1280x1826/1834, ~245KB)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:20:26 -08:00
hailin
c7978f6fb5
fix(mining-wallet-service): pool-account GET :type 改为 @Public() 允许内网服务间调用
...
mining-admin-service 通过 fetch 查询余额时无 token,@AdminOnly() 导致 401,UI 始终显示 0.00
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 09:21:39 -08:00
hailin
17ecc9954f
fix(admin-service): 认种筛选改用 PlantingOrderQueryView 实时查询
...
UserQueryView.personalAdoptionCount 预计算字段未被事件同步更新(始终为 0),
导致前端选"已认种"后查不到数据。
修复:认种数过滤(minAdoptions/maxAdoptions)不再查 UserQueryView 的
stale 字段,改为实时 groupBy 查询 PlantingOrderQueryView
(status='MINING_ENABLED'),与 getBatchUserStats 展示数据源保持一致。
- 已认种(minAdoptions>=1):accountSequence IN 有订单的集合
- 未认种(maxAdoptions=0):accountSequence NOT IN 有订单的集合
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 08:09:12 -08:00
hailin
126169c631
fix(injection): findUnique 改为 findFirst 避免命名唯一索引类型错误
...
Prisma @@unique 单字段命名索引 uk_adoption_id 的 TypeScript 类型
与 findUnique where 输入不兼容(TS2322)。改用 findFirst + where
字段查询,走同一个唯一索引,性能相同。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 06:14:13 -08:00
hailin
1baed76d8e
fix(injection): docker-compose 添加注入钱包环境变量 + 未配置时不启用消费者
...
问题:
.env 配置了 FUSDT_INJECTION_WALLET_USERNAME 和 FUSDT_INJECTION_WALLET_ADDRESS,
但 docker-compose.2.0.yml 没有将这两个变量传入容器,导致容器内 process.env
拿不到值,Adoption Injection Consumer 启动后报错。
修复:
1. docker-compose.2.0.yml: 添加 FUSDT_INJECTION_WALLET_USERNAME/ADDRESS
和 CDC_TOPIC_CONTRIBUTION_OUTBOX 环境变量传递
2. AdoptionInjectionConsumerService: onModuleInit 检查钱包是否配置,
未配置时跳过 Kafka 连接(不浪费消费者组资源)
3. AdoptionInjectionHandler: 检查 Consumer 是否启用,未启用时不注册
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 06:08:48 -08:00
hailin
c93eeba79a
fix(injection): findUnique 使用命名唯一索引 uk_adoption_id
...
Prisma 的 @@unique 使用自定义名 "uk_adoption_id" 后,
findUnique 的 where 必须用 { uk_adoption_id: { adoptionId } }
而非 { adoptionId },否则报 PrismaClientValidationError。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 06:05:26 -08:00
hailin
8980a169ed
fix(trading): 修复做市商吃单扣款金额错误(使用交易总额而非原始数量×价格)
...
问题:
做市商吃单时,buyerPayAmount = tradeQuantity × price(原始数量×价格),
未计入销毁倍数。例如:卖出 697.81 积分股,销毁倍数 5000.88,
交易总额 = 17,615.96 积分值,但做市商只被扣了 697.81 × 0.005 = 3.49。
10% 手续费(1,761.60)反而大于做市商支出,导致做市商不减反增。
修复:
- 做市商吃单时:actualBuyerCost = sellerGrossAmount(含销毁倍数的交易总额)
- 普通用户买入时:actualBuyerCost = buyerPayAmount(原始数量×价格,不变)
- frozenCash 仍释放下单时冻结的原始金额(buyerPayAmount)
- 做市商交易流水记录 assetType=CASH + 实际支付金额
做市商吃单扣款计算(修复后):
MM 支出 = sellerGrossAmount(交易总额)
MM 手续费收入 = tradeFee(10%)
MM 净支出 = sellerReceiveAmount = 交易总额 × 90%
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 05:56:12 -08:00
hailin
000e8f7ef1
fix(trading): 修复积分股重复记账,做市商吃单积分股只进流通池
...
问题:
之前所有卖出成交时,积分股同时记入了买方账户和流通池,
导致同一笔积分股被重复计入两处(双重记账)。
修复:
- 做市商吃单时:积分股只进入流通池,不增加做市商 shareBalance
(目前不允许做市商挂卖单,做市商不需要持有积分股)
- 普通用户买入时:积分股直接进入买方账户,不再重复计入流通池
- 将 mmConfig 查询提到循环外复用,去掉循环内冗余查询
积分股流向(修复后):
做市商吃单: 卖方 -X → 流通池 +X(做市商 shareBalance 不变)
用户间交易: 卖方 -X → 买方 +X(流通池不变)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 05:47:16 -08:00
hailin
4817d92507
feat(adoption-injection): 认种自动 fUSDT 注入做市商钱包 + CDC outbox 修复
...
## contribution-service 改动
### 1. CDC Outbox 实时发布修复
- adoption-synced.handler: handleCreate/handleUpdate 在同步数据后立即写入
AdoptionSynced outbox 事件,确保 mining-admin-service 实时接收认种同步
- referral-synced.handler: 同理,写入 ReferralSynced outbox 事件
- 之前只有手动调用 /admin/adoptions/publish-all 才会创建 outbox 事件
### 2. 认种 fUSDT 注入事件
- 新增 AdoptionFusdtInjectionRequestedEvent 事件类
- 当认种状态变为 MINING_ENABLED 时触发
- 写入 outbox,topic: contribution.adoptionfusdtinjectionrequested
- payload 含: adoptionId, accountSequence, treeCount, adoptionDate, amount
- 转账金额 = treeCount × 5760 fUSDT
## mining-blockchain-service 改动
### 3. fUSDT 注入钱包 MPC 签名支持
- mpc-signing.client: 新增 FUSDT_INJECTION_WALLET_USERNAME/ADDRESS 配置
isFusdtInjectionConfigured(), signMessageAsFusdtInjection() 等方法
- erc20-transfer.service: IMpcSigningClient 接口增加注入钱包方法
新增 transferFusdtAsInjectionWallet() 转账方法(含余额检查、MPC签名、广播)
### 4. 认种注入 Kafka 消费者
- adoption-injection-consumer.service: 订阅 cdc.contribution.outbox
过滤 AdoptionFusdtInjectionRequested 事件,解析 Debezium 扁平化消息格式
- adoption-injection.handler: 处理注入事件
- 幂等性检查(已确认的跳过)
- 写入 adoption_injection_records 分类账(PROCESSING 状态)
- 从注入钱包转 fUSDT 到做市商钱包
- 成功: markConfirmed (txHash, blockNumber, gasUsed)
- 失败: markFailed (errorMessage),支持重试
- 发布 confirmed/failed Kafka 事件
### 5. 分类账持久化
- Prisma schema: 新增 AdoptionInjectionRecord 模型 (adoption_injection_records 表)
字段: adoption_id, account_sequence, tree_count, adoption_date, from/to_address,
amount, chain_type, tx_hash, block_number, gas_used, memo, status, error_message
索引: uk_adoption_id (唯一), idx_injection_account, idx_injection_status 等
- migration: 20250203000000_add_adoption_injection_records
- Repository 接口 + 实现: save, findByAdoptionId, markConfirmed, markFailed
### 6. 启动余额检查
- main.ts: 启动时异步查询注入钱包 fUSDT 余额,余额为 0 时输出警告日志
新增注入钱包配置验证
## 部署前需添加环境变量 (.env)
FUSDT_INJECTION_WALLET_USERNAME=wallet-bff20b69
FUSDT_INJECTION_WALLET_ADDRESS=0x7BDB89dA47F16869c90446C41e70A00dDc432DBB
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 05:25:56 -08:00
hailin
4a69fdd070
fix(trading): getConfig 自动从环境变量填充空的钱包地址
...
之前只有 initializeConfig 会读取 env 填充钱包地址,
getConfig 只读数据库。导致新增 eusdtWalletAddress 字段后
即使 .env 配置了地址,前端仍显示"未配置"。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 04:09:00 -08:00
hailin
2192f5e917
fix(trading): 修复迁移表名 MarketMakerConfig → market_maker_configs
...
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 03:50:52 -08:00
hailin
ebe7123583
feat(market-maker): 积分股(eUSDT)和积分值(fUSDT)使用独立 MPC 钱包
...
- Prisma: MarketMakerConfig 新增 eusdt_wallet_address 字段
- 后端: 初始化/更新配置时读取 EUSDT_MARKET_MAKER_ADDRESS 环境变量
- 后端: getConfig API 返回 eusdtWalletAddress
- 前端: 积分股充值弹窗使用 eusdtWalletAddress (不再共用 kavaWalletAddress)
- 前端: 积分值充值弹窗继续使用 kavaWalletAddress (fUSDT 钱包)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 03:39:54 -08:00
hailin
38ee808239
fix(wallet): 池余额不足时跳过 Kafka 消息,避免无限重试
...
BurnConsumer 和 MiningDistributionConsumer 在池余额为 0 时抛出
Insufficient pool balance 错误导致 offset 不提交,造成无限重试。
现改为记录 warn 日志并标记事件为已处理(带 :skipped:insufficient_balance 标记)。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 03:28:34 -08:00
hailin
8cfd107a92
refactor(pool-accounts): 移除硬编码钱包名,改为从后端 API 动态获取
...
- 后端: 新增 GET /admin/pool-accounts 接口,返回从 .env 配置的池账户列表
- 前端: 新增 usePoolAccounts hook,动态渲染池账户卡片
- 前端: 提取 PoolAccountCard 子组件,消除重复代码
- 移除前端硬编码的 BURN_POOL_WALLET_NAME / MINING_POOL_WALLET_NAME 常量
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:57:40 -08:00
hailin
7972163af6
fix(docker): 移除池账户钱包硬编码默认值,改为从 .env 读取
...
所有池账户钱包配置(USERNAME/ADDRESS)不再有硬编码默认值,
必须在 .env 中配置才会生效。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:54:00 -08:00
hailin
c89c8769d9
fix(docker): 设置池账户钱包地址默认值
...
从 .env.example 获取池账户 MPC 钱包地址作为 docker-compose 默认值:
- BURN_POOL_WALLET_ADDRESS: 0xdE2932D2A25e1698c1354A41e2e46B414C46F5a1
- MINING_POOL_WALLET_ADDRESS: 0x8BC9091375ae8ef43ae011F0f9bAf10e51bC9D59
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:48:58 -08:00
hailin
a8098d801b
fix(docker): 添加池账户 MPC 钱包环境变量到 mining-blockchain-service
...
为100亿销毁池和200万挖矿池添加 BURN_POOL_WALLET_USERNAME/ADDRESS
和 MINING_POOL_WALLET_USERNAME/ADDRESS 环境变量配置。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:42:55 -08:00
hailin
a5f6b23a95
feat(pool-account): 池账户充值弹窗新增中心化充值 tab
...
与做市商管理一致,充值弹窗包含"中心化充值"和"区块链充值"两个 tab。
中心化充值直接调整池账户余额(ADJUSTMENT 类型分类账),无需区块链交易。
变更:
- wallet-service: pool-account.service 新增 centralizedDeposit 方法
- wallet-service: pool-account.controller 新增 POST /centralized-deposit
- admin-service: pool-account.controller 新增 POST /:walletName/centralized-deposit 代理
- frontend: configs.api + use-configs hook + configs page 充值弹窗 Tabs UI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:26:31 -08:00
hailin
c51539e494
fix(docker): 补充 mining-admin-service 池账户相关环境变量
...
添加 MINING_WALLET_SERVICE_URL、MINING_BLOCKCHAIN_SERVICE_URL、
BURN_POOL_WALLET_USERNAME、MINING_POOL_WALLET_USERNAME 到
docker-compose.2.0.yml 的 mining-admin-service 配置中。
缺少这些变量导致池账户 API 的 walletName→poolType 映射为空,
返回 400 Bad Request。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:18:35 -08:00
hailin
46f85b13b0
fix(pool-account): 修复 seed.ts 覆盖池余额 + 激活挖矿前校验池余额
...
1. seed.ts upsert 的 update 子句移除 balance/targetBurn/remainingBurn,
避免重新部署时将已有池余额清零(导致 BurnConsumer 和
MiningDistributionConsumer 因余额不足崩溃)
2. activateMining 激活前检查 SHARE_POOL_A/B 余额,任一为0则阻止激活
并返回 400 错误提示先通过区块链充值
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 02:00:59 -08:00
hailin
4f5b18be48
feat(pool-account): 实现池账户区块链充值检测和提现功能
...
为100亿销毁池和200万挖矿池实现完整的区块链充值/提现流程:
- 充值:独立扫描器检测 fUSDT 转入 → Kafka 事件 → wallet-service 入账
- 提现:前端 → admin-service 代理 → blockchain-service MPC签名转账 → wallet-service 记账
mining-blockchain-service:
- 新增 PoolAccountDeposit/PoolAccountBlockCheckpoint Prisma 模型
- 新增 PoolAccountDepositDetectionService(每5秒扫描 fUSDT 充值,每30秒更新确认数)
- 扩展 MPC 签名客户端支持 burnPool/miningPool 2-of-3 门限钱包
- 扩展 ERC20TransferService 支持池账户 fUSDT 转账
- 新增 API: POST /transfer/pool-account, GET /pool-accounts/:poolType/wallet-info
- 新增 Kafka topic 映射: pool_account.deposit.confirmed → pool_account.deposits
mining-wallet-service:
- TransactionType 枚举新增 BLOCKCHAIN_DEPOSIT/BLOCKCHAIN_WITHDRAW
- Seed: SHARE_POOL_A/B 初始余额改为0(完全靠链上充值)
- PoolAccountService 新增 blockchainDeposit()/blockchainWithdraw() 方法
- 新增 PoolAccountDepositConsumer 监听 Kafka 充值确认事件(Redis+DB 双重幂等)
- 新增 POST /pool-accounts/blockchain-withdraw 内部 API
mining-admin-service:
- 新增 PoolAccountController 代理到 wallet-service + blockchain-service
- GET /admin/pool-accounts/:walletName/balance(并行查询链下余额+链上钱包信息)
- POST /admin/pool-accounts/:walletName/blockchain-withdraw(先链上转账再记账)
- 新增配置: MINING_WALLET_SERVICE_URL, MINING_BLOCKCHAIN_SERVICE_URL, 池钱包用户名
frontend (mining-admin-web):
- 池账户 API 从 tradingClient (→trading-service) 改为 apiClient (→admin-service)
- 移除未使用的 tradingClient 和 axios 依赖
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 01:34:06 -08:00
hailin
4d2bcc7568
fix(mining-admin): 池账户钱包配置移到后端 .env,前端从 API 读取
...
做市商的 kavaWalletAddress 是后端 API 返回的,池账户应该一样。
- 后端 mining-blockchain-service/.env.example: 新增
BURN_POOL_WALLET_USERNAME/ADDRESS 和
MINING_POOL_WALLET_USERNAME/ADDRESS(同做市商钱包配置模式)
- 前端 .env.production: 移除 NEXT_PUBLIC_BURN/MINING_POOL 变量
- 前端 configs/page.tsx: 钱包地址改从 API 响应
(poolAccountBalance.walletAddress) 读取,未配置时显示提示
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 00:14:38 -08:00
hailin
999d0389b3
refactor(trading): 价格数据源从 SharePool 切换到做市商 cashBalance
...
积分股价格公式中的"绿积分"(分子)本质上就是做市商的积分值余额,
SharePool 是多余的抽象层。此次改动将价格计算的数据源从独立的
SharePool 表切换到做市商的 TradingAccount.cashBalance。
主要变更:
- price.service.ts: 通过 PrismaService 直接读取做市商 cashBalance
作为价格公式的分子(绿积分),移除 SharePoolRepository 依赖
- asset.service.ts: getMarketOverview() 同步切换数据源
- order.service.ts: 10%交易手续费从 sharePoolRepository.feeIn()
改为 increment 回做市商的 TradingAccount,手续费自然留存在池中
- burn.service.ts: 移除 SharePool 初始化逻辑和依赖
- infrastructure.module.ts: 移除 SharePoolRepository 注册和导出
- seed.ts: 移除 SharePool 57.6亿初始化
价格公式、10%手续费扣除、销毁机制均保持不变,仅切换数据源。
SharePool 表和 repository 文件保留在磁盘上供历史数据参考。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 23:13:59 -08:00
hailin
14e70b56bb
fix(c2c-bot): 水单完成时间转换为北京时间 (UTC+8)
...
问题:水单上的完成时间显示 UTC 时间(如 03:38:40),实际北京时间为 11:38:40。
原因:toISOString() 始终返回 UTC 时间。
修复:手动加 8 小时偏移量,将 UTC 转换为北京时间后再格式化。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 19:42:13 -08:00
hailin
bf11a269a4
fix(c2c-bot): 水单移除交易哈希,仅保留在分类账中
...
水单图片不再显示交易哈希(txHash),减少对终端用户暴露的链上细节。
txHash 仍保留在 ProofData 接口和数据库中,供分类账和审计使用。
同时缩小 SVG 高度(480→420)并重新排列剩余字段。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 19:35:48 -08:00
hailin
0fe0f3b72a
fix(c2c-bot): 水单显示优化:dUSDT→绿积分,收款地址→对方ID
...
水单图片调整:
- "支付金额 (dUSDT)" → "支付金额 (绿积分)"
- "收款地址"(显示 Kava 地址)→ "对方ID"(显示卖家账户 ID)
- ProofData 接口新增 sellerId 字段(来自 order.makerAccountSequence)
- sellerAddress 仍保留在数据结构中,供明细分类账使用
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 19:24:02 -08:00
hailin
7ee6d633c6
fix(kafka): 修复 MPC Kafka consumer 启动订阅失败后不重试的问题
...
问题现象:
- blockchain-service 启动时 Kafka consumer subscribe() 抛出
"This server does not host this topic-partition" 错误
- 原实现只 try-catch 一次就放弃,consumer 永久失效
- 后续所有 MPC 签名结果都收不到,表现为 signing timeout 300s
- 需要手动 docker restart 才能恢复
根因分析:
- 服务启动时 Kafka broker 可能尚未完成 topic-partition 分配
(特别是容器编排环境中服务启动顺序不确定)
- onModuleInit() 中的 connect/subscribe/run 是一次性调用
- KafkaJS 的 retry 配置只作用于内部操作,不覆盖初始连接流程
修复方案:
- 新增 connectWithRetry() 方法:指数退避重试(2s→4s→8s→...→60s)
最多 10 次,总等待约 5 分钟
- 每次重试前断开连接清理状态,避免 KafkaJS 内部状态残留
- 监听 consumer CRASH 事件:当 KafkaJS 不自动重启时(restart=false)
手动触发 connectWithRetry() 重连
- 新增 isShuttingDown 标志:防止 onModuleDestroy 时触发无意义的重连
- 同时修复 blockchain-service 和 identity-service 两个 consumer
影响范围:
- blockchain-service: 影响 MPC 签名结果接收(热钱包转账)
- identity-service: 影响 MPC 密钥生成结果接收(用户钱包创建)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 19:18:10 -08:00
hailin
2de4baf0af
fix(c2c-bot): 安装中文字体解决水单图片乱码问题
...
Alpine Linux 容器没有中文字体,sharp 渲染 SVG 时中文显示为方块。
- Dockerfile: 安装 fontconfig + font-noto-cjk 并刷新字体缓存
- SVG 模板: font-family 改为 Noto Sans CJK SC
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:56:55 -08:00
hailin
21f51c5d84
fix(c2c-bot): 修复 sharp 导入方式,解决水单生成 is not a function 错误
...
NestJS 使用 CommonJS 模块系统,`import sharp from 'sharp'` 编译后
生成 `sharp_1.default` 调用,但 sharp 没有 default export。
改为 `import * as sharp from 'sharp'` 修复。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:47:38 -08:00
hailin
eab61abace
feat(c2c-bot): 顺序处理订单 + 自动生成付款水单图片
...
1. 顺序处理订单:
- Scheduler 每个10s周期只处理1个卖单(原先最多10个)
- 移除 for 循环,确保完成一个订单后再处理下一个
- 分布式锁 TTL 从 8s 增加到 30s,留足链上转账时间
2. 付款水单自动生成:
- 新增 PaymentProofService,使用 SVG 模板 + sharp 转 PNG
- 水单包含:订单号、支付金额、交易哈希、收款地址、完成时间
- Bot 完成转账后自动生成水单并调用 updatePaymentProof 更新订单
- 水单生成失败不影响订单本身(try-catch 保护)
文件变更:
- package.json: 添加 sharp ^0.33.2 依赖
- c2c-bot.scheduler.ts: 限制每周期1单,增加锁时间
- payment-proof.service.ts: 新文件,SVG→PNG 水单生成服务
- application.module.ts: 注册 PaymentProofService
- c2c-bot.service.ts: 注入 PaymentProofService,步骤5生成水单
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:32:52 -08:00
hailin
7a1d438f84
fix(mpc): 去除 message_hash 的 0x 前缀,修复 MPC 签名 400 错误
...
MPC account-service 期望纯 hex 格式的 message_hash,不接受 0x 前缀。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:15:40 -08:00
hailin
cb8c69788b
fix(deploy): curl -s 改为 curl -sf,修复 HTTP 404 不被检测的问题
...
curl -s 只是静默输出,HTTP 404 仍然返回 exit code 0,导致就绪
检查在 Kafka Connect REST API 未初始化完时就通过了。
-f 标志让 curl 在 HTTP 4xx/5xx 时返回非零 exit code。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:30:37 -08:00
hailin
d8ef156b5e
fix(debezium): 修复 authorization connector 凭据和注册重试逻辑
...
- authorization-connector.json: 将硬编码的 debezium/debezium_password
改为 ${POSTGRES_USER}/${POSTGRES_PASSWORD} 占位符,与其他 connector 一致
- register-connectors.sh: 添加 3 次重试逻辑,应对 Kafka Connect REST API
初始化期间的间歇性 404
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:28:52 -08:00
hailin
5b3c391340
fix(infra): 添加 Zookeeper 数据持久化卷,修复 Kafka Cluster ID 冲突
...
Kafka 有持久化卷但 Zookeeper 没有,导致 down/up 后 ZK 生成新的
Cluster ID,与 Kafka meta.properties 中的旧 ID 冲突。
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:25:14 -08:00
hailin
d575287713
fix(deploy): 修复 deploy.sh Debezium connector 注册失败的问题
...
根因:就绪检查只检测 Jetty 根路径 /,但 Kafka Connect REST API
(/connectors) 需要更长时间初始化,导致注册时返回 404。
- deploy.sh: 就绪检查改为 /connectors,等待时间从 60s 增加到 120s
- deploy.sh: 删除 250 行硬编码的 connector 配置,改为调用
register-connectors.sh(单一数据源,JSON 文件)
- register-connectors.sh: 同步修复就绪检查
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 10:22:04 -08:00