v2.0: Supplement all guides to achieve 100% SRS coverage
Comprehensive additions across all 7 guide files (+2,439 lines): - 05-Backend: fee calculation (Maker-Taker), Breakage processing, refund mechanism, market maker system, three-factor pricing engine, AI/ML models (LightGBM/LSTM/IsolationForest), AML detection rules, OFAC sync service, Travel Rule (TRISA), tax compliance (1099/FATCA), CCPA/GDPR data deletion, security incident response (P0-P3), disaster recovery (RPO/RTO), mapping table security (MPC+audit+anchor), multi-currency, fiat hot-standby, chain reconciliation, capacity planning, SDK development plan, dispute handling, customer service - 06-Blockchain: GNX token economics, non-transferable coupon revert, differentiated KYC checks, validator-level OFAC/structuring/TravelRule interception, batch transfer, Treasury guarantee fund locking, contract upgrade rollback, multi-stablecoin (USDC+USDT), Oracle integration, asset securitization contract reserve - 01-Flutter Mobile: Pro mode (seed phrase, social recovery, AA wallet), MetaMask integration, external wallet extraction, transfer history, balance/deposit/withdraw, order history, phone number change, offline redemption limits/conflicts, dispute/complaint, notifications - 02-Flutter Admin: guarantee fund/frozen sales, reconciliation reports, secondary market analysis, financing effect analysis, refund window config, batch operations, coupon recall, multi-store hierarchy, dedicated customer service channel - 03-Web Admin: user behavior analytics, coupon category analysis, 1099 tax reports, FATCA, false advertising monitoring, SOX audit module, fee/revenue dashboard, settlement management, dispute arbitration, web merchant redemption console, market maker monitoring - 00-UI Design: Utility/Securities Track UI isolation design - 04-Mini Program: multi-language i18n (zh-CN/en-US/ja-JP), multi-currency display Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a0d2271cd5
commit
03e5f5b3e3
|
|
@ -456,6 +456,45 @@ AI Agent可代用户执行的操作(均需用户确认):
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本: v1.1*
|
---
|
||||||
|
|
||||||
|
## I. Utility Track / Securities Track UI隔离设计
|
||||||
|
|
||||||
|
> SRS 1.6要求:Utility Track和Securities Track使用不同的交易市场界面(前端隔离)。MVP阶段仅开放Utility Track。
|
||||||
|
|
||||||
|
### I1. 双轨UI隔离原则
|
||||||
|
|
||||||
|
| 维度 | Utility Track(消费型券) | Securities Track(投资型券) |
|
||||||
|
|------|--------------------------|---------------------------|
|
||||||
|
| **市场入口** | 默认市场(首页即Utility市场) | 独立入口(需KYC L2+才可见) |
|
||||||
|
| **价格展示** | 显示"折扣价"标签,面值上限提示 | 显示价格走势、K线图、涨跌幅 |
|
||||||
|
| **挂单界面** | 价格输入上限=面值,自动校验 | 无价格上限,自由定价 |
|
||||||
|
| **交易提示** | "您正在购买消费券用于消费" | "您正在进行投资交易,有亏损风险" |
|
||||||
|
| **转售计数** | 显示"剩余可转售X次" | 不显示限制 |
|
||||||
|
| **合规标识** | "消费券"标签(绿色) | "投资券"标签(橙色)+ 风险警示 |
|
||||||
|
| **KYC要求** | 交易前提示升级KYC L1 | 交易前提示升级KYC L2+ |
|
||||||
|
|
||||||
|
### I2. MVP阶段处理
|
||||||
|
|
||||||
|
```
|
||||||
|
MVP阶段:
|
||||||
|
- 只展示Utility Track市场
|
||||||
|
- Securities Track入口隐藏(代码预留,配置开关控制)
|
||||||
|
- 所有挂单价格自动校验 ≤ 面值
|
||||||
|
- 券详情页不显示"投资收益"相关信息
|
||||||
|
```
|
||||||
|
|
||||||
|
### I3. 关键UI差异点
|
||||||
|
|
||||||
|
| 页面 | Utility Track | Securities Track(预留) |
|
||||||
|
|------|--------------|------------------------|
|
||||||
|
| **券详情页** | 面值、折扣价、使用条件、发行方信用、到期日 | 同左 + 价格历史K线 + 涨跌幅 + 收益率 |
|
||||||
|
| **出售页面** | 价格滑块上限=面值,溢价时红色提示 | 无价格限制,显示市场参考价 |
|
||||||
|
| **持有目的声明** | 购买时弹窗确认"购买用于消费" | 购买时弹窗确认"我了解投资风险" |
|
||||||
|
| **我的券列表** | 标签:消费券(绿) | 标签:投资券(橙) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v1.2*
|
||||||
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
||||||
*更新: 增加AI Agent深度融合设计规范*
|
*更新: 增加AI Agent深度融合设计规范、Utility/Securities Track UI隔离设计*
|
||||||
|
|
|
||||||
|
|
@ -554,6 +554,319 @@ void main() {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本: v1.0*
|
## 13. Pro模式(加密原生用户)
|
||||||
|
|
||||||
|
### 13.1 Pro模式切换
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/profile/presentation/pages/pro_mode_settings.dart
|
||||||
|
class ProModeSettings extends ConsumerWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final user = ref.watch(userProfileProvider);
|
||||||
|
|
||||||
|
return Column(children: [
|
||||||
|
// 风险提示确认
|
||||||
|
RiskDisclosureCard(
|
||||||
|
text: '切换至Pro模式后,您将自行管理钱包私钥。'
|
||||||
|
'平台无法帮您恢复丢失的私钥或资产。',
|
||||||
|
),
|
||||||
|
|
||||||
|
// 开启Pro模式
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text('开启Pro模式'),
|
||||||
|
value: user.walletMode == WalletMode.pro,
|
||||||
|
onChanged: (enabled) async {
|
||||||
|
if (enabled) {
|
||||||
|
// 必须确认风险提示
|
||||||
|
final confirmed = await showRiskConfirmDialog(context);
|
||||||
|
if (confirmed) {
|
||||||
|
await ref.read(walletProvider.notifier).switchToProMode();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Pro→标准:需将资产转回平台托管
|
||||||
|
await ref.read(walletProvider.notifier).switchToStandard();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
if (user.walletMode == WalletMode.pro) ...[
|
||||||
|
// 链上地址显示
|
||||||
|
CopyableAddressField(address: user.chainAddress),
|
||||||
|
// WalletConnect连接
|
||||||
|
WalletConnectButton(),
|
||||||
|
// MetaMask连接
|
||||||
|
MetaMaskConnectButton(),
|
||||||
|
// 交易哈希查看入口
|
||||||
|
ListTile(
|
||||||
|
title: Text('查看链上交易'),
|
||||||
|
onTap: () => launchUrl(explorerUrl),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.2 助记词备份与社交恢复
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/wallet/domain/usecases/backup_usecase.dart
|
||||||
|
class BackupSeedPhraseUseCase {
|
||||||
|
/// Pro模式开启时强制提示备份助记词
|
||||||
|
Future<Either<Failure, void>> call() async {
|
||||||
|
final mnemonic = await _walletService.generateMnemonic();
|
||||||
|
// 显示12词助记词,用户手动抄写
|
||||||
|
// 验证确认(随机抽3个词验证)
|
||||||
|
return _walletService.confirmBackup(mnemonic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 社交恢复(Guardian机制)
|
||||||
|
class SocialRecoveryUseCase {
|
||||||
|
/// 预设3-5个可信联系人,多数确认即可恢复
|
||||||
|
Future<Either<Failure, void>> setupGuardians(List<String> guardianPhones) async {
|
||||||
|
if (guardianPhones.length < 3 || guardianPhones.length > 5) {
|
||||||
|
return Left(Failure.validation(errors: {'guardians': '需要3-5个守护人'}));
|
||||||
|
}
|
||||||
|
return _repo.setupSocialRecovery(guardianPhones);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发起恢复(需>50%守护人确认)
|
||||||
|
Future<Either<Failure, void>> initiateRecovery() async {
|
||||||
|
return _repo.initiateRecovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AA钱包(ERC-4337)集成
|
||||||
|
class AAWalletService {
|
||||||
|
/// 邮箱/手机号作为恢复入口
|
||||||
|
Future<void> recoverViaEmail(String email) async {
|
||||||
|
// ERC-4337 Account Abstraction恢复流程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 提取到外部钱包
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/wallet/presentation/pages/extract_to_external.dart
|
||||||
|
class ExtractToExternalPage extends ConsumerWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return Column(children: [
|
||||||
|
// KYC L2+才可提取
|
||||||
|
KycGuard(requiredLevel: KycLevel.L2, child: Column(children: [
|
||||||
|
// 输入外部钱包地址(EVM兼容)
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(labelText: '外部钱包地址(0x...)'),
|
||||||
|
controller: _addressController,
|
||||||
|
),
|
||||||
|
// 风险提示
|
||||||
|
WarningCard(
|
||||||
|
text: '提取后平台将不再托管该券。'
|
||||||
|
'您需自行保管钱包私钥,平台无法冻结、恢复或干预该券。',
|
||||||
|
),
|
||||||
|
// 选择要提取的券
|
||||||
|
CouponSelector(onSelected: (coupons) => setState(() => _selected = coupons)),
|
||||||
|
// 确认提取
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => _confirmExtract(ref),
|
||||||
|
child: Text('确认提取'),
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. 个人中心完整模块
|
||||||
|
|
||||||
|
### 15.1 转赠记录
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/transfer/presentation/pages/transfer_history.dart
|
||||||
|
class TransferHistoryPage extends ConsumerWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final transfers = ref.watch(transferHistoryProvider);
|
||||||
|
|
||||||
|
return transfers.when(
|
||||||
|
data: (list) => ListView.builder(
|
||||||
|
itemCount: list.length,
|
||||||
|
itemBuilder: (_, i) => TransferHistoryCard(
|
||||||
|
direction: list[i].direction, // sent / received
|
||||||
|
recipientDisplay: list[i].recipientPhone ?? '外部钱包',
|
||||||
|
couponName: list[i].couponName,
|
||||||
|
timestamp: list[i].createdAt,
|
||||||
|
status: list[i].status, // completed / pending
|
||||||
|
),
|
||||||
|
),
|
||||||
|
loading: () => SkeletonList(),
|
||||||
|
error: (e, _) => ErrorWidget(e),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 15.2 我的余额/充值/提现
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/wallet/domain/entities/balance.dart
|
||||||
|
@freezed
|
||||||
|
class WalletBalance with _$WalletBalance {
|
||||||
|
const factory WalletBalance({
|
||||||
|
required double totalBalance, // 总余额(链上+链下聚合,美元显示)
|
||||||
|
required double availableBalance, // 可用余额
|
||||||
|
required double frozenBalance, // 冻结金额(挂单中/待结算)
|
||||||
|
required double withdrawable, // 可提现
|
||||||
|
}) = _WalletBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充值/提现流程
|
||||||
|
class DepositUseCase {
|
||||||
|
Future<Either<Failure, DepositResult>> call(DepositParams params) {
|
||||||
|
// 银行卡/信用卡/Apple Pay → 法币入金 → 后台转换USDC
|
||||||
|
return _repo.deposit(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WithdrawUseCase {
|
||||||
|
Future<Either<Failure, WithdrawResult>> call(WithdrawParams params) {
|
||||||
|
// USDC → 法币 → 银行卡(T+1到账)
|
||||||
|
return _repo.withdraw(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 15.3 我的订单
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/trading/presentation/pages/order_history.dart
|
||||||
|
class OrderHistoryPage extends ConsumerWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final orders = ref.watch(orderHistoryProvider);
|
||||||
|
return DefaultTabController(
|
||||||
|
length: 3,
|
||||||
|
child: Column(children: [
|
||||||
|
TabBar(tabs: [
|
||||||
|
Tab(text: '全部'),
|
||||||
|
Tab(text: '买入'),
|
||||||
|
Tab(text: '卖出'),
|
||||||
|
]),
|
||||||
|
Expanded(child: TabBarView(children: [
|
||||||
|
OrderList(orders: orders),
|
||||||
|
OrderList(orders: orders.where((o) => o.side == OrderSide.buy)),
|
||||||
|
OrderList(orders: orders.where((o) => o.side == OrderSide.sell)),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 15.4 换手机号
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/profile/domain/usecases/change_phone.dart
|
||||||
|
class ChangePhoneUseCase {
|
||||||
|
/// 换手机号:KYC身份验证(人脸+证件)后迁移账户
|
||||||
|
Future<Either<Failure, void>> call(ChangePhoneParams params) async {
|
||||||
|
// 1. 验证新手机号未注册
|
||||||
|
// 2. KYC身份验证(人脸识别 + 证件对比)
|
||||||
|
// 3. 旧手机号验证码确认
|
||||||
|
// 4. 更新映射表(旧手机→新手机,链上地址不变)
|
||||||
|
return _repo.changePhone(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. 离线核销增强
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/merchant/data/services/offline_redeem_service.dart
|
||||||
|
// 补充:离线核销限额与冲突处理
|
||||||
|
|
||||||
|
class OfflineRedeemConfig {
|
||||||
|
static const maxSingleAmount = 500.0; // 单笔离线核销限额$500
|
||||||
|
static const maxDailyAmount = 5000.0; // 单日离线核销限额$5,000
|
||||||
|
static const maxDailyCount = 50; // 单日离线核销笔数限制
|
||||||
|
}
|
||||||
|
|
||||||
|
class OfflineConflictResolver {
|
||||||
|
/// 冲突处理:同一张券被两个门店离线核销
|
||||||
|
/// 以先上链者为准,后者自动退回并通知
|
||||||
|
Future<void> resolveConflict(PendingRedemption local, ChainRedemption chain) async {
|
||||||
|
if (chain.redeemedBy != local.storeId) {
|
||||||
|
// 该券已被其他门店核销,本地核销无效
|
||||||
|
await _notifyStore(local.storeId, '券${local.couponCode}已被其他门店核销');
|
||||||
|
await _queue.delete(local.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. 争议与投诉
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/support/domain/entities/ticket.dart
|
||||||
|
@freezed
|
||||||
|
class SupportTicket with _$SupportTicket {
|
||||||
|
const factory SupportTicket({
|
||||||
|
required String id,
|
||||||
|
required TicketCategory category, // transaction/account/coupon
|
||||||
|
required String description,
|
||||||
|
required TicketStatus status,
|
||||||
|
String? orderId, // 关联订单号
|
||||||
|
List<String>? attachments,
|
||||||
|
}) = _SupportTicket;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TicketCategory { transaction, account, coupon, compliance, other }
|
||||||
|
enum TicketStatus { open, inProgress, waitingUser, resolved, closed }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. 消息通知
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// lib/features/notification/domain/entities/notification.dart
|
||||||
|
@freezed
|
||||||
|
class AppNotification with _$AppNotification {
|
||||||
|
const factory AppNotification({
|
||||||
|
required String id,
|
||||||
|
required NotificationType type,
|
||||||
|
required String title,
|
||||||
|
required String body,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required bool isRead,
|
||||||
|
String? deepLink, // 点击跳转(如券详情、订单详情)
|
||||||
|
}) = _AppNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NotificationType {
|
||||||
|
tradeComplete, // 交易完成
|
||||||
|
couponExpiringSoon, // 券即将过期
|
||||||
|
priceAlert, // 价格变动
|
||||||
|
transferReceived, // 收到转赠
|
||||||
|
systemAnnouncement, // 系统公告
|
||||||
|
issuerNotice, // 发行方公告
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v2.0*
|
||||||
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
||||||
*技术栈: Flutter 3.x + Riverpod + Clean Architecture*
|
*技术栈: Flutter 3.x + Riverpod + Clean Architecture*
|
||||||
|
*更新: 补充Pro模式/助记词/社交恢复/AA钱包/外部钱包提取/转赠记录/余额/订单/换手机号/离线核销增强/争议投诉/消息通知*
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,261 @@ class AiSuggestionCard extends StatelessWidget {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本: v1.0*
|
## 9. 财务管理增强
|
||||||
|
|
||||||
|
### 9.1 保证金与冻结销售款
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 发行方可选:缴纳保证金以快速提升额度
|
||||||
|
@freezed
|
||||||
|
class GuaranteeFund with _$GuaranteeFund {
|
||||||
|
const factory GuaranteeFund({
|
||||||
|
required double deposited, // 已缴纳保证金
|
||||||
|
required double frozenSales, // 冻结的销售款(自愿开启)
|
||||||
|
required bool autoFreezeEnabled, // 是否开启销售款自动冻结
|
||||||
|
required double freezePercent, // 冻结比例(如20%销售额)
|
||||||
|
}) = _GuaranteeFund;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 财务管理增强视图
|
||||||
|
@freezed
|
||||||
|
class FinanceDetailView with _$FinanceDetailView {
|
||||||
|
const factory FinanceDetailView({
|
||||||
|
required double salesRevenue, // 销售收入
|
||||||
|
required double breakageIncome, // Breakage收入(过期券)
|
||||||
|
required double platformFees, // 平台手续费支出
|
||||||
|
required double pendingSettlement, // 待结算
|
||||||
|
required double withdrawable, // 可提现
|
||||||
|
required double totalWithdrawn, // 已提现
|
||||||
|
required double guaranteeDeposit, // 保证金
|
||||||
|
required double frozenSales, // 冻结销售款
|
||||||
|
}) = _FinanceDetailView;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 对账报表
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 日/月对账单
|
||||||
|
class ReconciliationReport {
|
||||||
|
final DateTime periodStart;
|
||||||
|
final DateTime periodEnd;
|
||||||
|
final List<TransactionEntry> entries;
|
||||||
|
final double totalIncome;
|
||||||
|
final double totalExpense;
|
||||||
|
final double netBalance;
|
||||||
|
|
||||||
|
// 导出为CSV/PDF
|
||||||
|
Future<File> exportAsCsv() async { /* ... */ }
|
||||||
|
Future<File> exportAsPdf() async { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 发行方数据分析增强
|
||||||
|
|
||||||
|
### 10.1 二级市场分析
|
||||||
|
|
||||||
|
```dart
|
||||||
|
@freezed
|
||||||
|
class SecondaryMarketAnalysis with _$SecondaryMarketAnalysis {
|
||||||
|
const factory SecondaryMarketAnalysis({
|
||||||
|
required int listedCount, // 二级市场挂单数
|
||||||
|
required double avgResalePrice, // 平均转售价
|
||||||
|
required double avgDiscount, // 平均折扣率
|
||||||
|
required int resaleVolume, // 转售成交量
|
||||||
|
required double resaleRevenue, // 转售成交额
|
||||||
|
required List<PricePoint> priceHistory, // 价格历史(K线数据)
|
||||||
|
}) = _SecondaryMarketAnalysis;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 融资效果分析
|
||||||
|
|
||||||
|
```dart
|
||||||
|
@freezed
|
||||||
|
class FinancingEffectReport with _$FinancingEffectReport {
|
||||||
|
const factory FinancingEffectReport({
|
||||||
|
required double cashAdvanced, // 现金提前回笼总额
|
||||||
|
required double avgAdvanceDays, // 平均提前回笼天数
|
||||||
|
required double financingCost, // 融资成本(平台手续费+折扣损失)
|
||||||
|
required double effectiveRate, // 等效年利率
|
||||||
|
required List<CashFlowPoint> cashFlowTimeline, // 现金流时序图
|
||||||
|
}) = _FinancingEffectReport;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 退款窗口配置
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 发行方可配置退款期限
|
||||||
|
@freezed
|
||||||
|
class RefundPolicy with _$RefundPolicy {
|
||||||
|
const factory RefundPolicy({
|
||||||
|
required int refundWindowDays, // 退款窗口(天,默认7天)
|
||||||
|
required bool autoRefundEnabled, // 是否自动退款
|
||||||
|
required String refundConditions, // 退款条件说明
|
||||||
|
}) = _RefundPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建券时配置退款策略
|
||||||
|
class CreateCouponWithRefundPolicy {
|
||||||
|
Widget buildRefundSection() {
|
||||||
|
return Column(children: [
|
||||||
|
Text('退款策略'),
|
||||||
|
NumberPicker(
|
||||||
|
label: '退款窗口(天)',
|
||||||
|
min: 0, max: 30, initial: 7,
|
||||||
|
onChanged: (days) => _refundDays = days,
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text('允许自动退款'),
|
||||||
|
subtitle: Text('窗口期内用户可直接退款无需审核'),
|
||||||
|
value: _autoRefund,
|
||||||
|
onChanged: (v) => setState(() => _autoRefund = v),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 批量操作
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 批量发行、批量核销、批量导出
|
||||||
|
class BatchOperationsService {
|
||||||
|
// 批量发行(一次创建多种券模板)
|
||||||
|
Future<Either<Failure, List<CouponDraft>>> batchCreate(
|
||||||
|
List<CreateCouponParams> paramsList
|
||||||
|
) async {
|
||||||
|
return _repo.batchSubmitDrafts(paramsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量核销
|
||||||
|
Future<Either<Failure, BatchRedeemResult>> batchRedeem(
|
||||||
|
List<String> couponCodes
|
||||||
|
) async {
|
||||||
|
return _repo.batchRedeem(couponCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量数据导出
|
||||||
|
Future<File> exportData({
|
||||||
|
required ExportType type, // coupons / transactions / reconciliation
|
||||||
|
required DateRange range,
|
||||||
|
required ExportFormat format, // csv / xlsx / pdf
|
||||||
|
}) async {
|
||||||
|
return _repo.exportBatchData(type, range, format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 券召回与下架
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 券召回流程
|
||||||
|
class CouponRecallService {
|
||||||
|
/// 召回未售出的券(链上销毁)
|
||||||
|
Future<Either<Failure, RecallResult>> recallUnsold(String couponBatchId) async {
|
||||||
|
return _repo.recallUnsoldCoupons(couponBatchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 问题券紧急下架
|
||||||
|
Future<Either<Failure, void>> emergencyDelist(String couponId, String reason) async {
|
||||||
|
return _repo.delistCoupon(couponId, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 已售出券的退款处理
|
||||||
|
Future<Either<Failure, void>> initiateRefundForSold(
|
||||||
|
String couponBatchId, String refundReason
|
||||||
|
) async {
|
||||||
|
return _repo.initiateRefundForSoldCoupons(couponBatchId, refundReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 多门店管理增强
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 门店层级管理
|
||||||
|
@freezed
|
||||||
|
class StoreHierarchy with _$StoreHierarchy {
|
||||||
|
const factory StoreHierarchy({
|
||||||
|
required String id,
|
||||||
|
required String name,
|
||||||
|
required StoreLevel level, // headquarters / regional / store
|
||||||
|
String? parentId, // 上级ID
|
||||||
|
required List<StoreHierarchy> children,
|
||||||
|
required StorePermissions permissions,
|
||||||
|
}) = _StoreHierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StoreLevel { headquarters, regional, store }
|
||||||
|
|
||||||
|
// 门店员工管理
|
||||||
|
@freezed
|
||||||
|
class StoreEmployee with _$StoreEmployee {
|
||||||
|
const factory StoreEmployee({
|
||||||
|
required String id,
|
||||||
|
required String name,
|
||||||
|
required String phone,
|
||||||
|
required StoreRole role, // cashier / manager / admin
|
||||||
|
required List<String> storeIds, // 可操作的门店
|
||||||
|
}) = _StoreEmployee;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StoreRole {
|
||||||
|
cashier, // 收银员:仅核销权限
|
||||||
|
manager, // 店长:核销 + 数据查看
|
||||||
|
admin, // 管理员:全部权限
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. 专属客服通道
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// 铂金/钻石层级发行方专属客服
|
||||||
|
class DedicatedSupportWidget extends ConsumerWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final issuer = ref.watch(issuerProfileProvider);
|
||||||
|
|
||||||
|
if (['platinum', 'diamond'].contains(issuer.tier.name)) {
|
||||||
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.headset_mic, color: Colors.amber),
|
||||||
|
title: Text('专属客服'),
|
||||||
|
subtitle: Text('1小时内响应 | 您的客户经理:${issuer.accountManager}'),
|
||||||
|
onTap: () => _openDedicatedChat(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.support_agent),
|
||||||
|
title: Text('在线客服'),
|
||||||
|
subtitle: Text('24小时内响应'),
|
||||||
|
onTap: () => _openSupportTicket(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v2.0*
|
||||||
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
||||||
*技术栈: Flutter 3.x + Riverpod + Clean Architecture*
|
*技术栈: Flutter 3.x + Riverpod + Clean Architecture*
|
||||||
|
*更新: 补充保证金/冻结销售款/对账报表/二级市场分析/融资效果/退款窗口/批量操作/券召回/多门店增强/专属客服*
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,354 @@ CMD ["npm", "start"]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本: v1.0*
|
## 10. 数据报表与分析
|
||||||
|
|
||||||
|
### 10.1 平台数据报表
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/reports/types.ts
|
||||||
|
interface PlatformReport {
|
||||||
|
type: 'daily' | 'monthly' | 'quarterly';
|
||||||
|
metrics: {
|
||||||
|
totalTransactionVolume: number;
|
||||||
|
totalTransactionAmount: number;
|
||||||
|
activeUsers: number;
|
||||||
|
newIssuers: number;
|
||||||
|
avgDiscountRate: number;
|
||||||
|
platformRevenue: number; // 手续费收入
|
||||||
|
breakageRevenue: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 用户行为分析
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/analytics/user-analytics.ts
|
||||||
|
interface UserBehaviorAnalytics {
|
||||||
|
registrationFunnel: FunnelData; // 注册→KYC→首次交易转化率
|
||||||
|
retentionRate: RetentionData; // 留存率(日/周/月)
|
||||||
|
activeUserTrend: TimeSeriesData; // DAU/WAU/MAU
|
||||||
|
tradingPatterns: TradingPatternData; // 交易行为模式
|
||||||
|
kycLevelDistribution: PieData; // KYC等级分布
|
||||||
|
walletModeDistribution: PieData; // 标准/Pro模式占比
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.3 券类别分析
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/analytics/coupon-analytics.ts
|
||||||
|
interface CouponCategoryAnalytics {
|
||||||
|
byIndustry: CategoryBreakdown[]; // 按行业(餐饮/购物/娱乐)
|
||||||
|
byTemplate: CategoryBreakdown[]; // 按模板(满减/折扣/礼品卡/储值)
|
||||||
|
byIssuerTier: CategoryBreakdown[]; // 按发行方层级
|
||||||
|
redemptionByCategory: BarData; // 各品类兑付率
|
||||||
|
breakageByCategory: BarData; // 各品类Breakage率
|
||||||
|
priceDistribution: HistogramData; // 价格分布
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 税务合规报表
|
||||||
|
|
||||||
|
### 11.1 IRS 1099报表管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/compliance/tax-reports.ts
|
||||||
|
interface Tax1099Management {
|
||||||
|
// IRS Form 1099-DA/1099-B 生成与管理
|
||||||
|
generateBatch: (taxYear: number) => Promise<Tax1099Report[]>;
|
||||||
|
reviewReport: (reportId: string) => Promise<Tax1099Report>;
|
||||||
|
submitToIRS: (reportIds: string[]) => Promise<SubmissionResult>;
|
||||||
|
userPortalExport: (userId: string, taxYear: number) => Promise<TaxDocument>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1099报表页面
|
||||||
|
export function Tax1099Page() {
|
||||||
|
const [taxYear, setTaxYear] = useState(2025);
|
||||||
|
const reports = useQuery(['tax1099', taxYear], () => taxApi.getReports(taxYear));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<YearSelector value={taxYear} onChange={setTaxYear} />
|
||||||
|
<StatsCards>
|
||||||
|
<Card title="需生成1099" value={reports.pendingCount} />
|
||||||
|
<Card title="已生成" value={reports.generatedCount} />
|
||||||
|
<Card title="已提交IRS" value={reports.submittedCount} />
|
||||||
|
</StatsCards>
|
||||||
|
<Button onClick={() => taxApi.generateBatch(taxYear)}>批量生成1099</Button>
|
||||||
|
<TanStackTable data={reports.data} columns={tax1099Columns} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.2 FATCA跨境税务
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface FatcaReport {
|
||||||
|
foreignAccountHolders: ForeignUser[];
|
||||||
|
totalUsSourceIncome: number;
|
||||||
|
reportingStatus: 'pending' | 'submitted';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 消费者保护与虚假宣传监控
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/compliance/consumer-protection.ts
|
||||||
|
interface ConsumerProtectionCase {
|
||||||
|
id: string;
|
||||||
|
type: 'misleading_description' | 'false_advertising' | 'undisclosed_conditions';
|
||||||
|
couponId: string;
|
||||||
|
issuerId: string;
|
||||||
|
reportedBy: 'user' | 'ai_scan' | 'admin';
|
||||||
|
evidence: {
|
||||||
|
couponDescription: string; // 券描述
|
||||||
|
actualRedemptionInfo: string; // 实际兑付情况
|
||||||
|
discrepancy: string; // 差异说明
|
||||||
|
};
|
||||||
|
status: 'reported' | 'investigating' | 'confirmed' | 'dismissed';
|
||||||
|
action: 'warning' | 'delist' | 'suspend_issuer' | 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI扫描券描述一致性
|
||||||
|
export function FalseAdvertisingMonitor() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>虚假宣传监控</h3>
|
||||||
|
{/* AI自动扫描所有上架券的描述 vs 实际兑付记录 */}
|
||||||
|
<AlertList alerts={consumerProtectionAlerts} />
|
||||||
|
{/* 手动审查队列 */}
|
||||||
|
<CaseQueue cases={pendingCases} onResolve={handleResolve} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. SOX审计模块
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/compliance/sox-audit.ts
|
||||||
|
interface SoxAuditDashboard {
|
||||||
|
// Section 302: CEO/CFO认证追踪
|
||||||
|
certifications: CertificationRecord[];
|
||||||
|
// Section 404: 内部控制评估
|
||||||
|
internalControls: InternalControlAssessment[];
|
||||||
|
// 智能合约升级审计追踪
|
||||||
|
contractUpgradeAudit: ContractUpgradeLog[];
|
||||||
|
// 链上记录与GAAP对账
|
||||||
|
chainToGaapReconciliation: ReconciliationReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ContractUpgradeLog {
|
||||||
|
proposalId: string;
|
||||||
|
contractName: string;
|
||||||
|
oldImplementation: string;
|
||||||
|
newImplementation: string;
|
||||||
|
proposedBy: string;
|
||||||
|
approvedBy: string[]; // 多签审批人
|
||||||
|
timelockExpiry: Date;
|
||||||
|
executedAt: Date;
|
||||||
|
auditReport: string; // 第三方审计报告链接
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOX审计页面
|
||||||
|
export function SoxAuditPage() {
|
||||||
|
return (
|
||||||
|
<Tabs>
|
||||||
|
<Tab label="内部控制">
|
||||||
|
<InternalControlMatrix />
|
||||||
|
</Tab>
|
||||||
|
<Tab label="合约升级日志">
|
||||||
|
<ContractUpgradeAuditTrail />
|
||||||
|
</Tab>
|
||||||
|
<Tab label="链上对账">
|
||||||
|
<ChainGaapReconciliation />
|
||||||
|
</Tab>
|
||||||
|
<Tab label="操作审计日志">
|
||||||
|
<AuditLogViewer /> {/* 全链路操作日志,append-only */}
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 财务管理
|
||||||
|
|
||||||
|
### 14.1 手续费管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/finance/fee-management.ts
|
||||||
|
interface FeeConfiguration {
|
||||||
|
tradingFees: {
|
||||||
|
takerBuy: number; // 0.5%
|
||||||
|
takerSell: number;
|
||||||
|
makerBuy: number; // 0.1%
|
||||||
|
makerSell: number;
|
||||||
|
};
|
||||||
|
issuanceFees: Record<IssuerTier, number>;
|
||||||
|
breakageSharePercent: number; // 平台Breakage分润比例
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手续费收入仪表盘
|
||||||
|
export function FeeRevenueDashboard() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<RevenueCards>
|
||||||
|
<Card title="交易手续费" value={tradingFeeRevenue} />
|
||||||
|
<Card title="发行服务费" value={issuanceFeeRevenue} />
|
||||||
|
<Card title="Breakage分润" value={breakageRevenue} />
|
||||||
|
<Card title="增值服务" value={vasRevenue} />
|
||||||
|
</RevenueCards>
|
||||||
|
<RevenueChart data={revenueTimeSeries} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 14.2 结算管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 发行方结算管理
|
||||||
|
interface SettlementManagement {
|
||||||
|
pendingSettlements: SettlementBatch[];
|
||||||
|
completedSettlements: SettlementBatch[];
|
||||||
|
issuerBalances: IssuerBalance[];
|
||||||
|
withdrawalRequests: WithdrawalRequest[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. 争议与仲裁处理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/disputes/types.ts
|
||||||
|
interface DisputeCase {
|
||||||
|
id: string;
|
||||||
|
type: 'buyer_complaint' | 'seller_complaint' | 'refund_request';
|
||||||
|
status: 'submitted' | 'evidence_collection' | 'arbitration' | 'resolved' | 'escalated';
|
||||||
|
orderId: string;
|
||||||
|
buyerId: string;
|
||||||
|
sellerId: string;
|
||||||
|
chainEvidence: {
|
||||||
|
txHash: string;
|
||||||
|
blockNumber: number;
|
||||||
|
timestamp: Date;
|
||||||
|
transferRecord: string;
|
||||||
|
};
|
||||||
|
sla: {
|
||||||
|
responseDeadline: Date; // 24h响应
|
||||||
|
resolutionDeadline: Date; // 72h处理
|
||||||
|
};
|
||||||
|
arbitrationDecision?: {
|
||||||
|
decidedBy: string;
|
||||||
|
decision: 'refund' | 'reject' | 'partial_refund';
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 争议处理页面
|
||||||
|
export function DisputeManagementPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SlaOverview />
|
||||||
|
<DisputeList
|
||||||
|
columns={[
|
||||||
|
{ key: 'id', title: '工单号' },
|
||||||
|
{ key: 'type', title: '类型' },
|
||||||
|
{ key: 'status', title: '状态' },
|
||||||
|
{ key: 'sla', title: 'SLA倒计时', render: (sla) => <SlaCountdown deadline={sla.resolutionDeadline} /> },
|
||||||
|
{ key: 'actions', title: '操作', render: (_, row) => <ArbitrationActions dispute={row} /> },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Web商户核销后台
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/app/(dashboard)/merchant-redemption/page.tsx
|
||||||
|
// 门店管理员通过浏览器核销+查看核销记录
|
||||||
|
export default function MerchantRedemptionPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Web端核销后台</h2>
|
||||||
|
|
||||||
|
{/* 输码核销 */}
|
||||||
|
<Card title="核销券">
|
||||||
|
<Input placeholder="输入券码" />
|
||||||
|
<Button type="primary">查询并核销</Button>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 核销记录 */}
|
||||||
|
<Card title="核销记录">
|
||||||
|
<DateRangePicker />
|
||||||
|
<TanStackTable
|
||||||
|
data={redemptionRecords}
|
||||||
|
columns={[
|
||||||
|
{ key: 'couponCode', title: '券码' },
|
||||||
|
{ key: 'couponName', title: '券名' },
|
||||||
|
{ key: 'faceValue', title: '面值' },
|
||||||
|
{ key: 'operator', title: '核销员' },
|
||||||
|
{ key: 'store', title: '门店' },
|
||||||
|
{ key: 'time', title: '核销时间' },
|
||||||
|
{ key: 'status', title: '状态' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 门店数据汇总 */}
|
||||||
|
<StoreRedemptionStats />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. 做市商管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/features/trading/market-maker-management.ts
|
||||||
|
interface MarketMakerManagement {
|
||||||
|
marketMakers: MarketMaker[];
|
||||||
|
obligations: MarketMakerObligation[];
|
||||||
|
performanceMetrics: MmPerformanceMetric[];
|
||||||
|
violationAlerts: MmViolation[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 做市商活动监控页面
|
||||||
|
export function MarketMakerMonitorPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>做市商监控</h3>
|
||||||
|
<MmPerformanceTable data={marketMakers} />
|
||||||
|
{/* Spoofing/Layering检测告警 */}
|
||||||
|
<ManipulationAlerts alerts={spoofingAlerts} />
|
||||||
|
{/* 做市义务合规检查 */}
|
||||||
|
<ObligationComplianceCheck />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v2.0*
|
||||||
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
||||||
*技术栈: React 18 + TypeScript 5 + Next.js 14 + Zustand + Redux Toolkit*
|
*技术栈: React 18 + TypeScript 5 + Next.js 14 + Zustand + Redux Toolkit*
|
||||||
|
*更新: 补充数据报表/用户行为分析/券类别分析/1099税务/FATCA/虚假宣传监控/SOX审计/财务管理/争议仲裁/Web核销后台/做市商管理*
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,87 @@ export default defineAppConfig({
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本: v1.0*
|
## 10. 多语言与国际化
|
||||||
|
|
||||||
|
### 10.1 i18n配置
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/utils/i18n.ts
|
||||||
|
import Taro from '@tarojs/taro';
|
||||||
|
|
||||||
|
// 支持语言:中文(默认)、英文、日文
|
||||||
|
type Locale = 'zh-CN' | 'en-US' | 'ja-JP';
|
||||||
|
|
||||||
|
const messages: Record<Locale, Record<string, string>> = {
|
||||||
|
'zh-CN': {
|
||||||
|
'home.title': '发现好券',
|
||||||
|
'coupon.buy': '立即购买',
|
||||||
|
'coupon.faceValue': '面值',
|
||||||
|
'coupon.discount': '折扣',
|
||||||
|
'coupon.expiry': '有效期至',
|
||||||
|
'my.coupons': '我的券',
|
||||||
|
'my.orders': '我的订单',
|
||||||
|
'redeem.show': '出示给商户扫码',
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
'home.title': 'Discover Deals',
|
||||||
|
'coupon.buy': 'Buy Now',
|
||||||
|
'coupon.faceValue': 'Face Value',
|
||||||
|
'coupon.discount': 'Discount',
|
||||||
|
'coupon.expiry': 'Valid Until',
|
||||||
|
'my.coupons': 'My Coupons',
|
||||||
|
'my.orders': 'My Orders',
|
||||||
|
'redeem.show': 'Show to Merchant',
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
'ja-JP': {
|
||||||
|
'home.title': 'クーポンを探す',
|
||||||
|
'coupon.buy': '今すぐ購入',
|
||||||
|
'coupon.faceValue': '額面',
|
||||||
|
'coupon.discount': '割引',
|
||||||
|
'coupon.expiry': '有効期限',
|
||||||
|
'my.coupons': 'マイクーポン',
|
||||||
|
'my.orders': '注文履歴',
|
||||||
|
'redeem.show': '店舗に提示',
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useI18n() {
|
||||||
|
const locale = useLocaleStore((s) => s.locale);
|
||||||
|
const t = (key: string) => messages[locale]?.[key] || messages['zh-CN'][key] || key;
|
||||||
|
return { t, locale };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动检测系统语言
|
||||||
|
export function detectLocale(): Locale {
|
||||||
|
const systemInfo = Taro.getSystemInfoSync();
|
||||||
|
const lang = systemInfo.language || 'zh-CN';
|
||||||
|
if (lang.startsWith('en')) return 'en-US';
|
||||||
|
if (lang.startsWith('ja')) return 'ja-JP';
|
||||||
|
return 'zh-CN';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 多币种展示
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/utils/currency.ts
|
||||||
|
export function formatPrice(amount: number, currency: string = 'USD'): string {
|
||||||
|
const formatters: Record<string, Intl.NumberFormat> = {
|
||||||
|
USD: new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }),
|
||||||
|
CNY: new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }),
|
||||||
|
JPY: new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }),
|
||||||
|
SGD: new Intl.NumberFormat('en-SG', { style: 'currency', currency: 'SGD' }),
|
||||||
|
};
|
||||||
|
return formatters[currency]?.format(amount) || `$${amount.toFixed(2)}`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v2.0*
|
||||||
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1*
|
||||||
*技术栈: Taro 3.x + React 18 + TypeScript*
|
*技术栈: Taro 3.x + React 18 + TypeScript*
|
||||||
|
*更新: 补充多语言国际化(i18n)、多币种展示*
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -556,6 +556,391 @@ forge verify-contract \
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本: v1.0*
|
## 12. GNX原生代币经济模型
|
||||||
|
|
||||||
|
### 12.1 代币用途(MVP阶段)
|
||||||
|
|
||||||
|
| 用途 | 说明 | MVP状态 |
|
||||||
|
|------|------|---------|
|
||||||
|
| **Gas消耗** | 支付交易费用(平台全额补贴) | 用户不接触 |
|
||||||
|
| **治理投票** | 参与链参数决策 | 仅平台内部 |
|
||||||
|
| **质押收益** | 验证节点质押获奖励 | 暂不开放 |
|
||||||
|
| **二级市场交易** | 交易所买卖GNX | 暂不开放 |
|
||||||
|
|
||||||
|
> MVP阶段GNX仅用于Gas(平台补贴),不上交易所,回避SEC证券风险。质押开放需取得法律意见书。
|
||||||
|
|
||||||
|
### 12.2 代币分配(预留设计)
|
||||||
|
|
||||||
|
```
|
||||||
|
总供应量: 1,000,000,000 GNX
|
||||||
|
├── 平台运营/Gas补贴: 40%(4亿)— 前期Gas支出从此池扣除
|
||||||
|
├── 团队与顾问: 20%(2亿)— 4年线性释放,1年锁定期
|
||||||
|
├── 生态基金: 15%(1.5亿)— 开发者激励、做市商激励
|
||||||
|
├── 未来融资预留: 15%(1.5亿)— Reg D/Reg S豁免发行
|
||||||
|
└── 社区治理: 10%(1亿)— DAO治理基金
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.3 Gas经济模型
|
||||||
|
|
||||||
|
```go
|
||||||
|
// genex-chain/x/evm/keeper/gas.go
|
||||||
|
// MVP阶段:Gas Price = 0,平台全额补贴
|
||||||
|
// 后期可通过Governance合约调整Gas参数
|
||||||
|
|
||||||
|
func (k Keeper) GetBaseFee(ctx sdk.Context) *big.Int {
|
||||||
|
params := k.GetParams(ctx)
|
||||||
|
if params.MinGasPrice.IsZero() {
|
||||||
|
return big.NewInt(0) // 免费Gas
|
||||||
|
}
|
||||||
|
return params.MinGasPrice.BigInt()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Coupon合约补充 — 不可转让券
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/Coupon.sol — 补充transfer限制逻辑
|
||||||
|
contract Coupon is ERC721Upgradeable, AccessControlUpgradeable {
|
||||||
|
mapping(uint256 => CouponConfig) private _configs;
|
||||||
|
|
||||||
|
/// @notice 重写transfer,不可转让券直接revert
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 tokenId,
|
||||||
|
uint256 /* batchSize */
|
||||||
|
) internal virtual override {
|
||||||
|
// 铸造(from=0)和销毁(to=0)不受限
|
||||||
|
if (from == address(0) || to == address(0)) return;
|
||||||
|
|
||||||
|
CouponConfig memory config = _configs[tokenId];
|
||||||
|
|
||||||
|
// 不可转让券:revert
|
||||||
|
require(config.transferable, "Coupon: non-transferable");
|
||||||
|
|
||||||
|
// 转售次数检查
|
||||||
|
require(
|
||||||
|
_resaleCount[tokenId] < config.maxResaleCount,
|
||||||
|
"Coupon: max resale count exceeded"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 批量转移(批量P2P/批量交易)
|
||||||
|
function batchTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256[] calldata tokenIds
|
||||||
|
) external {
|
||||||
|
for (uint256 i = 0; i < tokenIds.length; i++) {
|
||||||
|
safeTransferFrom(from, to, tokenIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Compliance合约补充 — 差异化KYC检查
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/Compliance.sol — 补充KYC等级差异化检查
|
||||||
|
contract Compliance is Initializable, AccessControlUpgradeable {
|
||||||
|
// KYC等级映射
|
||||||
|
mapping(address => uint8) private _kycLevels; // 0=L0, 1=L1, 2=L2, 3=L3
|
||||||
|
|
||||||
|
// 不同操作要求不同KYC等级
|
||||||
|
function requireKycLevel(address account, uint8 requiredLevel) public view {
|
||||||
|
require(_kycLevels[account] >= requiredLevel, "Compliance: insufficient KYC level");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 交易前合规检查(Settlement调用)
|
||||||
|
function preTradeCheck(
|
||||||
|
address buyer,
|
||||||
|
address seller,
|
||||||
|
uint256 amount,
|
||||||
|
CouponType couponType
|
||||||
|
) external view {
|
||||||
|
// OFAC黑名单检查
|
||||||
|
require(!_blacklist[buyer], "Buyer blacklisted");
|
||||||
|
require(!_blacklist[seller], "Seller blacklisted");
|
||||||
|
|
||||||
|
// Utility Track: 双方至少KYC L1
|
||||||
|
if (couponType == CouponType.Utility) {
|
||||||
|
requireKycLevel(buyer, 1);
|
||||||
|
requireKycLevel(seller, 1);
|
||||||
|
}
|
||||||
|
// Securities Track: 双方至少KYC L2
|
||||||
|
else {
|
||||||
|
requireKycLevel(buyer, 2);
|
||||||
|
requireKycLevel(seller, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 大额交易: KYC L2+
|
||||||
|
if (amount >= 10000e6) { // $10,000
|
||||||
|
requireKycLevel(buyer, 2);
|
||||||
|
requireKycLevel(seller, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 做市商: KYC L3
|
||||||
|
// (做市商身份由链下服务标记,链上通过MARKET_MAKER_ROLE验证)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice P2P转移合规路由
|
||||||
|
function p2pComplianceCheck(
|
||||||
|
address sender,
|
||||||
|
address receiver,
|
||||||
|
uint256 amount
|
||||||
|
) external {
|
||||||
|
require(!_blacklist[sender], "Sender blacklisted");
|
||||||
|
require(!_blacklist[receiver], "Receiver blacklisted");
|
||||||
|
|
||||||
|
// ≥$3,000 强制Travel Rule
|
||||||
|
if (amount >= 3000e6) {
|
||||||
|
requireKycLevel(sender, 2);
|
||||||
|
requireKycLevel(receiver, 2);
|
||||||
|
// Travel Rule数据必须已记录
|
||||||
|
require(_travelRuleRecorded[sender][receiver], "Travel Rule data required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. 验证节点级交易拦截
|
||||||
|
|
||||||
|
```go
|
||||||
|
// genex-chain/x/evm/ante/compliance_ante.go
|
||||||
|
// 验证节点在打包交易前执行合规检查
|
||||||
|
|
||||||
|
type ComplianceAnteHandler struct {
|
||||||
|
ofacList map[string]bool
|
||||||
|
travelRule TravelRuleChecker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h ComplianceAnteHandler) AnteHandle(
|
||||||
|
ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||||
|
) (sdk.Context, error) {
|
||||||
|
for _, msg := range tx.GetMsgs() {
|
||||||
|
evmMsg, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||||
|
if !ok { continue }
|
||||||
|
|
||||||
|
from := evmMsg.GetFrom()
|
||||||
|
to := evmMsg.GetTo()
|
||||||
|
|
||||||
|
// 1. OFAC地址拦截(链级强制)
|
||||||
|
if h.ofacList[from.Hex()] || h.ofacList[to.Hex()] {
|
||||||
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized,
|
||||||
|
"OFAC sanctioned address, transaction rejected at validator level")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Structuring检测(拆分交易规避$3,000阈值)
|
||||||
|
if h.isStructuringPattern(ctx, from, evmMsg.GetValue()) {
|
||||||
|
// 不拒绝,但标记为可疑(升级为Travel Rule流程)
|
||||||
|
ctx = ctx.WithValue("suspicious_structuring", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Travel Rule预打包检查
|
||||||
|
if evmMsg.GetValue().Cmp(big.NewInt(3000e6)) >= 0 {
|
||||||
|
if !h.travelRule.HasRecord(from, *to) {
|
||||||
|
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized,
|
||||||
|
"Travel Rule: identity data required for transfers >= $3,000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Treasury合约 — 保障资金锁定
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/Treasury.sol — 补充保障资金逻辑
|
||||||
|
contract Treasury is Initializable, AccessControlUpgradeable {
|
||||||
|
IERC20 public stablecoin;
|
||||||
|
|
||||||
|
// 发行方保障资金(自愿缴纳,提升信用评级)
|
||||||
|
mapping(address => uint256) public guaranteeFunds;
|
||||||
|
|
||||||
|
// 发行方冻结的销售款(自愿,作为兑付保障)
|
||||||
|
mapping(address => uint256) public frozenSalesRevenue;
|
||||||
|
|
||||||
|
/// @notice 发行方缴纳保障资金
|
||||||
|
function depositGuaranteeFund(uint256 amount) external {
|
||||||
|
stablecoin.transferFrom(msg.sender, address(this), amount);
|
||||||
|
guaranteeFunds[msg.sender] += amount;
|
||||||
|
emit GuaranteeFundDeposited(msg.sender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 发行方违约时启用保障资金赔付
|
||||||
|
function activateGuaranteeFund(
|
||||||
|
address issuer,
|
||||||
|
address[] calldata claimants,
|
||||||
|
uint256[] calldata amounts
|
||||||
|
) external onlyRole(GOVERNANCE_ROLE) {
|
||||||
|
uint256 totalClaim = 0;
|
||||||
|
for (uint256 i = 0; i < claimants.length; i++) {
|
||||||
|
totalClaim += amounts[i];
|
||||||
|
}
|
||||||
|
require(guaranteeFunds[issuer] >= totalClaim, "Insufficient guarantee fund");
|
||||||
|
|
||||||
|
guaranteeFunds[issuer] -= totalClaim;
|
||||||
|
for (uint256 i = 0; i < claimants.length; i++) {
|
||||||
|
stablecoin.transfer(claimants[i], amounts[i]);
|
||||||
|
}
|
||||||
|
emit GuaranteeFundActivated(issuer, totalClaim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 交易资金托管(原子交换中间态)
|
||||||
|
function escrow(
|
||||||
|
uint256 orderId,
|
||||||
|
address buyer,
|
||||||
|
uint256 amount
|
||||||
|
) external onlyRole(SETTLER_ROLE) {
|
||||||
|
stablecoin.transferFrom(buyer, address(this), amount);
|
||||||
|
emit FundsEscrowed(orderId, buyer, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 释放托管资金给卖方
|
||||||
|
function release(
|
||||||
|
uint256 orderId,
|
||||||
|
address seller,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 platformFee
|
||||||
|
) external onlyRole(SETTLER_ROLE) {
|
||||||
|
stablecoin.transfer(seller, amount - platformFee);
|
||||||
|
// 平台手续费归集
|
||||||
|
stablecoin.transfer(platformFeeCollector, platformFee);
|
||||||
|
emit FundsReleased(orderId, seller, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. 合约升级回滚能力
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/Governance.sol — 补充回滚能力
|
||||||
|
contract Governance is Initializable {
|
||||||
|
// 保留前一版Implementation地址
|
||||||
|
mapping(address => address) public previousImplementations;
|
||||||
|
|
||||||
|
/// @notice 紧急回滚至上一版本(需4/5多签)
|
||||||
|
function rollback(address proxy) external {
|
||||||
|
require(
|
||||||
|
_getApprovalCount(currentProposalId) >= 4,
|
||||||
|
"Rollback requires 4/5 multisig"
|
||||||
|
);
|
||||||
|
address prevImpl = previousImplementations[proxy];
|
||||||
|
require(prevImpl != address(0), "No previous version");
|
||||||
|
|
||||||
|
// 执行回滚
|
||||||
|
ITransparentUpgradeableProxy(proxy).upgradeTo(prevImpl);
|
||||||
|
emit ContractRolledBack(proxy, prevImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 升级时自动记录前一版本
|
||||||
|
function _recordPreviousImpl(address proxy, address newImpl) internal {
|
||||||
|
address currentImpl = ITransparentUpgradeableProxy(proxy).implementation();
|
||||||
|
previousImplementations[proxy] = currentImpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. 多稳定币支持
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/Settlement.sol — 补充多稳定币支持
|
||||||
|
contract Settlement is Initializable, AccessControlUpgradeable {
|
||||||
|
// 支持多种稳定币
|
||||||
|
mapping(address => bool) public supportedStablecoins;
|
||||||
|
// 默认稳定币
|
||||||
|
address public defaultStablecoin; // USDC
|
||||||
|
|
||||||
|
function addStablecoin(address token) external onlyRole(GOVERNANCE_ROLE) {
|
||||||
|
supportedStablecoins[token] = true;
|
||||||
|
emit StablecoinAdded(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice 原子交换(支持指定稳定币)
|
||||||
|
function executeSwap(
|
||||||
|
uint256 tokenId,
|
||||||
|
address buyer,
|
||||||
|
address seller,
|
||||||
|
uint256 price,
|
||||||
|
address stablecoin // 买方选择的稳定币
|
||||||
|
) external onlyRole(SETTLER_ROLE) {
|
||||||
|
require(supportedStablecoins[stablecoin], "Unsupported stablecoin");
|
||||||
|
|
||||||
|
// 合规检查 + 原子交换
|
||||||
|
// ...(同原有逻辑,使用传入的stablecoin地址)
|
||||||
|
IERC20(stablecoin).transferFrom(buyer, seller, price);
|
||||||
|
couponContract.safeTransferFrom(seller, buyer, tokenId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19. Oracle集成(汇率)
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/Oracle.sol — 汇率预言机集成
|
||||||
|
interface IChainlinkPriceFeed {
|
||||||
|
function latestRoundData() external view returns (
|
||||||
|
uint80 roundId, int256 answer, uint256 startedAt,
|
||||||
|
uint256 updatedAt, uint80 answeredInRound
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ExchangeRateOracle is Initializable {
|
||||||
|
// 各币种对USD汇率Feed
|
||||||
|
mapping(string => address) public priceFeeds;
|
||||||
|
|
||||||
|
function getRate(string calldata currency) external view returns (uint256) {
|
||||||
|
address feed = priceFeeds[currency];
|
||||||
|
require(feed != address(0), "Unsupported currency");
|
||||||
|
|
||||||
|
(, int256 answer,, uint256 updatedAt,) = IChainlinkPriceFeed(feed).latestRoundData();
|
||||||
|
// 汇率不能太旧(最多15分钟)
|
||||||
|
require(block.timestamp - updatedAt <= 900, "Stale price data");
|
||||||
|
return uint256(answer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 20. 资产证券化合约预留(Phase 4)
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// src/future/CouponBackedSecurity.sol — 预留设计
|
||||||
|
// Phase 4: 券收益流打包为CBS(Coupon-Backed Securities)
|
||||||
|
// 仅在Securities Track + Broker-Dealer牌照后启用
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice 预留接口定义,不在MVP实现
|
||||||
|
* - 券收益流打包
|
||||||
|
* - 信用评级接口
|
||||||
|
* - 收益曲线计算
|
||||||
|
*/
|
||||||
|
interface ICouponBackedSecurity {
|
||||||
|
function createPool(uint256[] calldata couponIds) external returns (uint256 poolId);
|
||||||
|
function getCreditRating(uint256 poolId) external view returns (string memory);
|
||||||
|
function getYieldCurve(uint256 poolId) external view returns (uint256[] memory);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v2.0*
|
||||||
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1 + 技术架构开发需求 v3.0*
|
*基于: Genex 券交易平台 - 软件需求规格说明书 v4.1 + 技术架构开发需求 v3.0*
|
||||||
*技术栈: Cosmos SDK + cosmos/evm + CometBFT + Solidity + Foundry*
|
*技术栈: Cosmos SDK + cosmos/evm + CometBFT + Solidity + Foundry*
|
||||||
|
*更新: 补充GNX代币经济/不可转让revert/差异化KYC/验证节点拦截/批量转移/Treasury保障资金/回滚能力/多稳定币/Oracle/资产证券化预留*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue