Commit Graph

108 Commits

Author SHA1 Message Date
hailin e328c75fc1 fix(pre-planting): 修复前后端 API 路径不匹配导致预种页面打不开
后端 PrePlantingController 缺少 eligibility 端点,前端请求 404 导致
Future.wait 整体失败,页面显示"加载数据失败"。

修复:
1. 后端: 在 PrePlantingController 添加 GET eligibility 端点
2. 前端: createOrder 路径从 /orders 改为 /purchase(匹配后端)
3. 前端: signMergeContract 路径从 /merges/:no/sign 改为 /sign-contract

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:07:47 -08:00
hailin 20a73a8d43 feat(planting-service): 添加 transfer_locked_count 列的数据库迁移
Prisma schema 中已定义 transferLockedCount 字段但缺少对应 migration,
导致运行时 PrismaClientKnownRequestError:
  The column planting_orders.transfer_locked_count does not exist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:57:15 -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 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 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 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 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 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 08cf4681f2 fix: migration 使用 IF NOT EXISTS 防止重复创建 + clean 去掉 --remove-orphans
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 00:37:29 -08:00
hailin 8326f8c35c fix(cdc): 添加 Debezium heartbeat 机制防止 WAL 堆积
问题背景:
- PostgreSQL pg_wal 目录从 80MB 膨胀到 60.4GB,导致磁盘使用率达到 96%
- 根因: wallet/planting/referral 三个数据库的业务表长期无写入
- 虽然 Debezium 有 heartbeat 配置,但未配置 heartbeat.action.query
- 导致 replication slot 的 restart_lsn 无法推进,WAL 文件无法被清理

解决方案:
1. 在 wallet/planting/referral 三个服务中添加 debezium_heartbeat 表
2. 配置 Debezium connector 的 heartbeat.action.query
3. 每 60 秒自动执行 UPDATE 语句推进 restart_lsn

修改内容:
- wallet-service/prisma/schema.prisma: 添加 DebeziumHeartbeat model
- planting-service/prisma/schema.prisma: 添加 DebeziumHeartbeat model
- referral-service/prisma/schema.prisma: 添加 DebeziumHeartbeat model
- scripts/debezium/wallet-connector.json: 添加 heartbeat.action.query 配置
- scripts/debezium/planting-connector.json: 添加 heartbeat.action.query 配置
- scripts/debezium/referral-connector.json: 添加 heartbeat.action.query 配置
- 新增三个服务的 Prisma migration 文件

效果:
- pg_wal 从 60.4GB 降至 80.2MB
- 磁盘使用率从 96% 降至 40%
- replication slot lag 从 51-60GB 降至 KB 级别

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 17:42:41 -08:00
hailin 51114f265d fix(planting-service): 修复合同PDF签署日期显示为UTC时间的问题
合同生成时使用 new Date().toISOString().split('T')[0] 获取日期,
该方法返回UTC时间,导致北京时间凌晨签署的合同显示为前一天日期。

修复方案:新增 getBeijingDateString() 函数,将UTC时间转换为北京时间(UTC+8)

影响范围:仅影响PDF合同上显示的签署日期,不影响数据库时间戳或业务逻辑

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:42:16 -08:00
hailin fe8e9a9bb6 fix(planting-service): 修复趋势数据查询表名错误
表名应为 planting_orders(复数),不是 planting_order

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 10:26:36 -08:00
hailin fa1931b3b6 feat(planting-service, admin-web): 实现认种趋势图表功能
后端变更 (planting-service):
- 添加 getTrendData API 接口支持按时间维度(日/周/月/季度/年)查询趋势数据
- 添加 TrendPeriod 类型和 TrendDataPoint 接口
- 实现 repository 层的趋势数据聚合查询

前端变更 (admin-web):
- 添加趋势数据 API 端点和类型定义
- 使用 recharts 实现折线图展示认种棵数和订单数趋势
- 支持日/周/月/季度/年度时间维度切换
- 添加加载状态、错误状态和空数据状态处理

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 10:21:11 -08:00
hailin 2be9a2d9c2 feat(statistics): 认种统计改为真实数据并显示积分
后端变更(planting-service):
- 添加 getMonthStats() 方法获取本月认种统计
- 更新 GlobalStatsResult 接口添加 monthStats 字段
- 添加 MonthStatsDto 响应类型

前端变更(admin-web):
- 更新 PlantingGlobalStats 类型定义
- statistics 页面调用真实 API 获取认种统计
- 显示认种总量、今日、本月的棵数和积分

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 09:36:46 -08:00
hailin 46b68e8652 feat(planting-service): add global stats API for data verification
Add new endpoint GET /api/v1/planting/stats/global to query planting
statistics directly from the database, providing reliable data source
for verifying reporting-service statistics.

New features:
- GlobalPlantingStats: total tree count, order count, amount
- StatusDistribution: breakdown by order status (PAID to MINING_ENABLED)
- TodayStats: daily statistics with tree count, order count, amount

Implementation:
- Pure additive changes, no modifications to existing code
- Read-only aggregate queries using Prisma aggregate/groupBy
- No database schema changes required

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 06:55:08 -08:00
hailin 5026661fa8 chore(planting): update contract PDF template to release version
Signature field position: x=449.51, y=140.18 (moved further right and up).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 02:45:28 -08:00
hailin bdc3cdd75e chore(planting): update contract PDF template to v1.2
Moved signature button field further right (x=435.60) and down (y=113.51).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 02:13:48 -08:00
hailin c8f2d5edff chore(planting): update contract PDF template to v1.1
Updated signature field position to x=427.60 for better alignment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 01:48:34 -08:00
hailin 67c7d9149c fix(planting): move signature field right to avoid overlapping text
Moved the signature field from x=415 to x=470 in the PDF template
to prevent the signature image from covering the "乙方(签字/盖章):" text.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 00:28:07 -08:00
hailin f5afb65df8 fix(planting): center signature image on the signature field
Calculate signature position based on field center instead of left-bottom
corner, so the signature image is properly centered within the field area.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 00:00:13 -08:00
hailin ef80a2f23b fix(planting): remove signature button field before flatten to avoid gray background
The signature button field has a gray background that covers the drawn
signature image when the form is flattened. Now we remove the signature
field after drawing the signature image to prevent this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 23:45:44 -08:00
hailin 083c0fd540 fix(planting): draw signature directly on page instead of using form field
The PDF signature field is only 92x51 points, which causes signatures to
appear too small or invisible. Changed to use drawImage() directly on
the page at the field's position with a larger size (150x80 max).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 23:28:18 -08:00
hailin 4a3658e770 chore(planting): update contract PDF template to v1
更新认种合同PDF模板为v1版本

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 22:43:15 -08:00
hailin 825b80b319 fix(planting): match PDF form field names to template
修改代码中的表单字段名以匹配PDF模板中的实际字段名:
- totalAmount → RmbAmount
- totalAmountChinese → SpellChineseFormatNumber
- greenPointsAmount → GreenAmount

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 21:33:14 -08:00
hailin 9c17140b33 feat(contract): update contract template with amount fields
更新合同模板和 PDF 生成服务,支持动态计算金额字段。

## 合同模板更新
- 替换为新版联合种植协议模板(3页,带公章)
- 新增表单字段:totalAmount、totalAmountChinese、greenPointsAmount

## PDF 生成服务更新
- 新增单价常量:
  - PRICE_PER_TREE_CNY = 17414.1(人民币含税价)
  - PRICE_PER_TREE_GREEN_POINTS = 15831(绿积分价格)
- 新增 numberToChineseAmount() 函数:数字转中文大写金额
- 更新 ContractPdfData 接口:新增可选字段 totalAmount、greenPointsAmount
- 更新 fillFormFields():根据认种棵数自动计算金额
- 移除坐标定位填充方式,仅使用表单字段方式
- 所有表单字段现为必需,缺少时抛出明确错误

## 金额计算逻辑
- 人民币金额 = 棵数 × 17414.1
- 绿积分金额 = 棵数 × 15831
- 大写金额自动生成(如:壹万柒仟肆佰壹拾肆元壹角)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 20:21:37 -08:00
hailin e9b2917561 fix(pdf-generator): 使用自定义外观流嵌入签名图片
- 恢复使用 widget.setNormalAppearance() 方式
- 创建 XObject Form 作为外观流
- 签名图片按字段尺寸等比例缩放并居中
- 不依赖页面索引,直接设置到widget上

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 07:58:44 -08:00
hailin 91d3e65289 fix(pdf-generator): 使用page.drawImage在按钮位置绘制签名图片
- 从git历史恢复正确的签名嵌入实现
- 获取signature按钮的widget和rectangle位置
- 按字段尺寸等比例缩放签名图片并居中
- 使用page.drawImage()绘制签名,而非setImage()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 07:54:17 -08:00
hailin 954f170bd4 feat(contract-signing): 增强签名功能
前端改进:
- 签名页面添加红色醒目提示"请使用正楷书写您的真实姓名"
- 签名前显示用户真实姓名供参照
- 实现全屏横向签名面板(自动切换横屏/竖屏)
- 记录签名轨迹数据(每个点的坐标和时间戳、笔画顺序)

后端改进:
- 扩展SignContractParams接口支持signatureTrace字段
- 控制器记录签名轨迹日志(笔画数、总时长)
- 签名轨迹数据以JSON格式存储,作为法律凭证

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 06:42:15 -08:00
hailin d4763ea5bf chore(planting-service): 更新合同PDF模板(修复红色文字)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 06:29:08 -08:00
hailin dfecdc06e9 chore(planting-service): 更新合同PDF模板
使用新的榴莲树认种权益协议_发布版_form.pdf替换原模板

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 06:15:49 -08:00
hailin ad5b153fa9 fix(pdf-generator): 签名按字段尺寸等比例缩放并居中
- 计算宽度和高度的缩放比例,取较小值确保签名完全在字段内
- 在字段内居中放置签名图片
- 符合行业标准:签名根据字段尺寸自适应缩放

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 05:56:38 -08:00
hailin 2e65a92e04 fix(planting-service): 签名图片按比例缩放到合适大小
- 目标宽度 150pt(约 5cm)
- 保持宽高比不变
- 避免签名图片过大

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 05:53:51 -08:00
hailin 6c017d2086 fix(planting-service): 签名图片保持原始尺寸放置
- 不再缩放签名图片适应按钮大小
- 直接在按钮位置绘制原始尺寸的签名
- 避免签名被压扁变形

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 05:53:22 -08:00
hailin 666be6ea60 fix(planting-service): 签名后查看合同返回已签名的PDF
- 修改 /tasks/:orderNo/pdf 接口,检查任务状态
- 如果已签名且有 signedPdfUrl,从 MinIO 下载已签名的 PDF
- 添加 downloadSignedPdf 方法到 MinioStorageService
- 在 ContractSigningTaskDto 中添加 signedPdfUrl 字段

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 05:48:22 -08:00
hailin 86103e4c4d fix(planting-service): 使用自定义外观流嵌入签名图片
- setImage 无法正确渲染签名到按钮字段
- 手动创建 XObject Form 外观流
- 计算图片缩放和居中位置
- 设置 widget 的 NormalAppearance

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 05:35:58 -08:00
hailin bdeff3b372 fix(planting-service): 修复合同签名无法放到指定位置的问题
- 修改 generateSignedContractPdf 在同一个 PDFDocument 实例上完成填充和签名
- 移除 fillFormFields 中的 form.flatten(),保留签名字段供后续使用
- 最后统一扁平化所有表单字段,确保签名放到正确位置
- 控制器改用 generateSignedContractPdf 替代分两步调用

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 05:23:53 -08:00
hailin 1d6982c73e feat(planting-service): 合同签署超时时间从24小时改为1年 2025-12-26 03:58:56 -08:00
hailin 9addd99710 fix(planting-service): 修正导入路径
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 01:50:37 -08:00
hailin 24a46521f3 fix(planting-service): 修复跨服务调用使用错误标识符导致的500错误
问题根源:
- getBalance 调用使用 userId.toString() (纯数字如 "14")
- wallet-service 按 accountSequence 查找钱包失败后尝试创建新钱包
- 但 userId 已存在,触发唯一约束冲突导致500错误

修复内容:
1. planting-application.service.ts:
   - createOrder: getBalance(userId.toString()) → getBalance(accountSequence)
   - payOrder: getBalance(userId.toString()) → getBalance(walletIdentifier)

2. payment-compensation.service.ts:
   - 注入 IPlantingOrderRepository 获取订单的 accountSequence
   - handleUnfreeze/handleRetryConfirm 添加 accountSequence 参数

3. wallet-service.client.ts:
   - ensureRegionAccounts 接口添加 provinceTeamAccount/cityTeamAccount 字段

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 01:48:22 -08:00
hailin f62a96f3f1 feat(planting): 已付款未KYC用户强制进入实名认证流程
后端 (planting-service):
- 添加 /contract-signing/kyc-requirement 接口检查用户是否需要KYC
- 检查已付款订单但无合同签署任务的情况

前端 (mobile-app):
- ContractCheckService 新增 checkAll() 综合检查方法
- HomeShellPage 综合检查待签署合同和KYC需求
- 需要KYC时弹出强制认证弹窗,不可关闭

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 21:14:53 -08:00
hailin 1e15b820b4 fix(planting-service): 合同PDF显示完整手机号和身份证号
- 移除 PDF 生成时对手机号和身份证号的脱敏处理
- 表单字段模式和坐标定位模式都直接使用原始值
- 删除 maskIdCard() 和 maskPhone() 脱敏方法
- 签订合同需要显示完整信息

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 18:55:55 -08:00
hailin 7a1e789f4d fix(contract): 合同签署页面和模板优化
1. 合同模板:身份证号和联系方式显示完整信息,不再使用星号掩码
2. 签署页面:checkbox 默认不选中,用户阅读到底部后才可点击确认

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 09:31:39 -08:00
hailin 63ac0debf3 feat(planting-service): 添加合同签署后事件恢复定时任务
每2~5分钟随机间隔扫描已签署超过2分钟的合同
重新发布 contract.signed 事件,确保扣款确认和奖励分配完成

幂等性已由 wallet-service 保证

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 09:08:47 -08:00
hailin 484cc99636 fix(planting-service): 修复 identity-service 响应解析
identity-service 响应被 TransformInterceptor 包装为 { success, data }
需要从 response.data.data 获取实际用户信息

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 08:47:43 -08:00
hailin 48f4ed60a6 fix(planting-service): 修复 identity-service API 调用路径
添加 /api/v1 前缀以匹配 identity-service 的全局路由配置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 08:39:45 -08:00
hailin c907f44851 feat(planting-service): 订单表添加 accountSequence,实现合同恢复任务
变更内容:
1. 订单表添加 account_sequence 字段
   - 创建订单时保存用户的 accountSequence
   - 避免跨服务调用 identity-service 获取用户信息

2. 新增 ContractSigningRecoveryJob 定时任务
   - 每 3 分钟扫描已支付但未创建合同的订单
   - 使用订单中的 accountSequence 获取 KYC 信息
   - 为已通过 KYC 的用户补创建合同签署任务

3. 修改 PlantingOrder 聚合根
   - create() 方法增加 accountSequence 参数
   - markAsPaid() 不再需要 accountSequence 参数
   - 事件中携带 accountSequence

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 08:19:36 -08:00