852 lines
27 KiB
Dart
852 lines
27 KiB
Dart
|
|
// 功能描述:A1 自动测试与截图共用的明确 Fixture,禁止作为运行入口使用。
|
|||
|
|
// 版本:1.0.0
|
|||
|
|
import 'package:user_app/data/repositories/client_repository.dart';
|
|||
|
|
import 'package:user_app/data/services/api_client.dart';
|
|||
|
|
import 'package:user_app/domain/models/client_models.dart';
|
|||
|
|
import 'package:user_app/domain/models/shipping_address.dart';
|
|||
|
|
import 'package:user_app/domain/models/product_detail.dart';
|
|||
|
|
import 'package:user_app/domain/models/cart_item.dart';
|
|||
|
|
import 'package:user_app/domain/models/product_favorite.dart';
|
|||
|
|
import 'package:user_app/domain/models/primary_models.dart';
|
|||
|
|
import 'package:user_app/domain/models/product_recommendations.dart';
|
|||
|
|
import 'package:user_app/domain/models/shop_order_detail.dart';
|
|||
|
|
import 'package:user_app/domain/models/gas_order_detail.dart';
|
|||
|
|
import 'package:user_app/domain/models/gas_order_checkout.dart';
|
|||
|
|
import 'package:user_app/domain/models/wallet_bill.dart';
|
|||
|
|
import 'package:user_app/domain/models/wallet_account.dart';
|
|||
|
|
import 'package:user_app/domain/models/deposit.dart';
|
|||
|
|
import 'package:user_app/domain/models/recharge.dart';
|
|||
|
|
import 'package:user_app/domain/models/delivery_detail.dart';
|
|||
|
|
import 'package:user_app/domain/models/delivery_track.dart';
|
|||
|
|
import 'package:user_app/domain/models/usage_statistics.dart';
|
|||
|
|
import 'package:user_app/domain/models/message_center.dart';
|
|||
|
|
import 'package:user_app/domain/models/family_sharing.dart';
|
|||
|
|
|
|||
|
|
/// 提供可控数据及失败状态,测试不依赖本地数据库。
|
|||
|
|
class A1FixtureRepository extends ClientRepository {
|
|||
|
|
A1FixtureRepository() : super(ApiClient(() => ''));
|
|||
|
|
bool fail = false;
|
|||
|
|
int withdrawalCalls = 0;
|
|||
|
|
int depositReturnCalls = 0;
|
|||
|
|
int gasOrderCreateCalls = 0;
|
|||
|
|
final markedMessageKeys = <String>[];
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<FamilyDashboardData> familyDashboard() async => FamilyDashboardData(
|
|||
|
|
householdName: '薛海路之家',
|
|||
|
|
ownerName: '张先生',
|
|||
|
|
memberCount: 3,
|
|||
|
|
deviceCount: 3,
|
|||
|
|
members: [
|
|||
|
|
const FamilyMember(
|
|||
|
|
identity: 'owner',
|
|||
|
|
name: '张先生',
|
|||
|
|
phoneMasked: '138****5678',
|
|||
|
|
relationship: '房主',
|
|||
|
|
inviteStatus: 20,
|
|||
|
|
isOwner: true,
|
|||
|
|
permissions: [],
|
|||
|
|
),
|
|||
|
|
const FamilyMember(
|
|||
|
|
identity: 'member-1',
|
|||
|
|
name: '张女士',
|
|||
|
|
phoneMasked: '139****6078',
|
|||
|
|
relationship: '家人',
|
|||
|
|
inviteStatus: 20,
|
|||
|
|
isOwner: false,
|
|||
|
|
permissions: [
|
|||
|
|
FamilyDevicePermission(
|
|||
|
|
deviceIdentity: 'device-1',
|
|||
|
|
deviceName: '厨房角阀',
|
|||
|
|
deviceKind: 'valve',
|
|||
|
|
canView: true,
|
|||
|
|
canAlert: true,
|
|||
|
|
canControl: true,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
const FamilyMember(
|
|||
|
|
identity: 'member-2',
|
|||
|
|
name: '李先生',
|
|||
|
|
phoneMasked: '138****0028',
|
|||
|
|
relationship: '房东',
|
|||
|
|
inviteStatus: 20,
|
|||
|
|
isOwner: false,
|
|||
|
|
permissions: [
|
|||
|
|
FamilyDevicePermission(
|
|||
|
|
deviceIdentity: 'device-2',
|
|||
|
|
deviceName: '厨房报警器',
|
|||
|
|
deviceKind: 'alarm',
|
|||
|
|
canView: true,
|
|||
|
|
canAlert: true,
|
|||
|
|
canControl: false,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
FamilyMember(
|
|||
|
|
identity: 'member-3',
|
|||
|
|
name: '王女士',
|
|||
|
|
phoneMasked: '137****9056',
|
|||
|
|
relationship: '家人',
|
|||
|
|
inviteStatus: 10,
|
|||
|
|
isOwner: false,
|
|||
|
|
permissions: const [],
|
|||
|
|
expiresAt: DateTime(2026, 9, 20),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
devices: const [
|
|||
|
|
FamilyDevice(
|
|||
|
|
identity: 'device-1',
|
|||
|
|
name: '厨房角阀',
|
|||
|
|
kind: 'valve',
|
|||
|
|
shareCount: 3,
|
|||
|
|
mappingConfigured: true,
|
|||
|
|
),
|
|||
|
|
FamilyDevice(
|
|||
|
|
identity: 'device-2',
|
|||
|
|
name: '厨房报警器',
|
|||
|
|
kind: 'alarm',
|
|||
|
|
shareCount: 3,
|
|||
|
|
mappingConfigured: true,
|
|||
|
|
),
|
|||
|
|
FamilyDevice(
|
|||
|
|
identity: 'device-3',
|
|||
|
|
name: '客厅报警器',
|
|||
|
|
kind: 'alarm',
|
|||
|
|
shareCount: 2,
|
|||
|
|
mappingConfigured: true,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<MessageCenterData> messages() async => MessageCenterData(
|
|||
|
|
counts: const {'all': 5, 'unread': 2, 'safety': 1, 'order': 2, 'service': 1, 'notice': 1},
|
|||
|
|
items: [
|
|||
|
|
UserMessage(
|
|||
|
|
key: 'safety-1',
|
|||
|
|
category: 'safety',
|
|||
|
|
title: '厨房燃气浓度异常',
|
|||
|
|
summary: '厨房报警器1:检测到燃气浓度异常',
|
|||
|
|
statusText: '已解除',
|
|||
|
|
target: '',
|
|||
|
|
targetIdentity: '',
|
|||
|
|
occurredAt: DateTime(2026, 9, 12, 9, 18),
|
|||
|
|
read: false,
|
|||
|
|
),
|
|||
|
|
UserMessage(
|
|||
|
|
key: 'order-2',
|
|||
|
|
category: 'order',
|
|||
|
|
title: '厨房角阀已远程关闭',
|
|||
|
|
summary: '厨房角阀已为您远程关闭,保障用气安全',
|
|||
|
|
statusText: '已完成',
|
|||
|
|
target: '',
|
|||
|
|
targetIdentity: '',
|
|||
|
|
occurredAt: DateTime(2026, 9, 12, 8, 52),
|
|||
|
|
read: true,
|
|||
|
|
),
|
|||
|
|
UserMessage(
|
|||
|
|
key: 'service-1',
|
|||
|
|
category: 'service',
|
|||
|
|
title: '报修工单已受理',
|
|||
|
|
summary: '您的报修工单已受理,工程师将尽快与您联系',
|
|||
|
|
statusText: '进行中',
|
|||
|
|
target: 'ticket',
|
|||
|
|
targetIdentity: 'ticket-1',
|
|||
|
|
occurredAt: DateTime(2026, 9, 11, 16, 30),
|
|||
|
|
read: true,
|
|||
|
|
),
|
|||
|
|
UserMessage(
|
|||
|
|
key: 'order-1',
|
|||
|
|
category: 'order',
|
|||
|
|
title: '配送员已出发',
|
|||
|
|
summary: '您的燃气商品订单配送状态已更新',
|
|||
|
|
statusText: '配送中',
|
|||
|
|
target: 'gas_order',
|
|||
|
|
targetIdentity: 'gas-1',
|
|||
|
|
occurredAt: DateTime(2026, 9, 3, 9, 42),
|
|||
|
|
read: false,
|
|||
|
|
),
|
|||
|
|
UserMessage(
|
|||
|
|
key: 'notice-1',
|
|||
|
|
category: 'notice',
|
|||
|
|
title: '秋季安全用气提醒',
|
|||
|
|
summary: '天气转凉,请注意用气通风,定期检查燃气设备。',
|
|||
|
|
statusText: '平台公告',
|
|||
|
|
target: 'content',
|
|||
|
|
targetIdentity: 'notice-1',
|
|||
|
|
occurredAt: DateTime(2026, 9, 1, 10),
|
|||
|
|
read: true,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<void> markMessagesRead(List<String> keys) async => markedMessageKeys.addAll(keys);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> devices() async => const [
|
|||
|
|
ClientRecord(
|
|||
|
|
identity: 'device-1',
|
|||
|
|
title: '厨房15kg气瓶',
|
|||
|
|
subtitle: 'GZ-15-0286',
|
|||
|
|
raw: {'kind': 'valve'},
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<UsageStatistics> usageStatistics({
|
|||
|
|
required String deviceIdentity,
|
|||
|
|
required String period,
|
|||
|
|
DateTime? anchor,
|
|||
|
|
}) async {
|
|||
|
|
final start = DateTime(2026, 9, 1);
|
|||
|
|
final values = [
|
|||
|
|
0.12,
|
|||
|
|
0.20,
|
|||
|
|
0.13,
|
|||
|
|
0.15,
|
|||
|
|
0.08,
|
|||
|
|
0.16,
|
|||
|
|
0.30,
|
|||
|
|
0.40,
|
|||
|
|
0.28,
|
|||
|
|
0.31,
|
|||
|
|
0.42,
|
|||
|
|
0.30,
|
|||
|
|
0.29,
|
|||
|
|
0.20,
|
|||
|
|
0.28,
|
|||
|
|
0.35,
|
|||
|
|
0.20,
|
|||
|
|
0.27,
|
|||
|
|
0.39,
|
|||
|
|
0.33,
|
|||
|
|
0.19,
|
|||
|
|
0.32,
|
|||
|
|
0.49,
|
|||
|
|
0.62,
|
|||
|
|
0.35,
|
|||
|
|
0.29,
|
|||
|
|
0.37,
|
|||
|
|
0.31,
|
|||
|
|
0.17,
|
|||
|
|
0.23,
|
|||
|
|
];
|
|||
|
|
return UsageStatistics(
|
|||
|
|
available: true,
|
|||
|
|
deviceIdentity: deviceIdentity,
|
|||
|
|
deviceName: '厨房15kg气瓶',
|
|||
|
|
deviceCode: 'GZ-15-0286',
|
|||
|
|
period: period,
|
|||
|
|
periodStart: start,
|
|||
|
|
periodEnd: DateTime(2026, 9, 30),
|
|||
|
|
unit: 'kg',
|
|||
|
|
points: [
|
|||
|
|
for (var index = 0; index < values.length; index++)
|
|||
|
|
UsagePoint(
|
|||
|
|
date: start.add(Duration(days: index)),
|
|||
|
|
usage: values[index],
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
totalUsage: values.fold(0.0, (sum, value) => sum + value),
|
|||
|
|
averageUsage: values.fold(0.0, (sum, value) => sum + value) / values.length,
|
|||
|
|
composition: const UsageComposition(breakfast: 2.2, lunch: 2.5, dinner: 3.9),
|
|||
|
|
source: 'device',
|
|||
|
|
calcVersion: 'v1',
|
|||
|
|
updatedAt: DateTime(2026, 9, 30, 23, 50),
|
|||
|
|
unavailableReason: '',
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<DeliveryDetail> gasOrderDelivery(String identity) async => DeliveryDetail.fromJson({
|
|||
|
|
'identity': identity,
|
|||
|
|
'order_no': '178509050920001',
|
|||
|
|
'status_code': 33,
|
|||
|
|
'status_name': '配送中',
|
|||
|
|
'status_message': '配送员正在前往您指定地点,请留意电话或消息通知',
|
|||
|
|
'appointment_at': '2026-09-05T10:30:00+08:00',
|
|||
|
|
'track_updated_at': '2026-09-05T09:56:00+08:00',
|
|||
|
|
'address': '北京市朝阳区望京中环南路2号星源国际B座1201室',
|
|||
|
|
'contact_name': '张女士',
|
|||
|
|
'contact_phone_masked': '138****5678',
|
|||
|
|
'station_name': '薛海气站',
|
|||
|
|
'delivery_name': '薛海气站配送点',
|
|||
|
|
'delivery_address': '北京市朝阳区薛海路8号',
|
|||
|
|
'staff_name': '张师傅',
|
|||
|
|
'staff_avatar': 'asset:assets/staff/delivery-worker.png',
|
|||
|
|
'staff_assigned': true,
|
|||
|
|
'credential_verified': true,
|
|||
|
|
'credential_type': 'hazmat',
|
|||
|
|
'track_available': true,
|
|||
|
|
'controlled_call_available': false,
|
|||
|
|
'message_available': false,
|
|||
|
|
'vehicle_configured': false,
|
|||
|
|
'products': [
|
|||
|
|
{'name': '15kg液化气', 'quantity': 1},
|
|||
|
|
],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<DeliveryTrack> gasOrderDeliveryTrack(String identity) async => DeliveryTrack.fromJson({
|
|||
|
|
'identity': identity,
|
|||
|
|
'status_code': 33,
|
|||
|
|
'status_name': '配送中',
|
|||
|
|
'status_message': '配送员正在为您配送,请耐心等待',
|
|||
|
|
'appointment_at': '2026-09-05T10:30:00+08:00',
|
|||
|
|
'updated_at': '2026-09-05T09:56:00+08:00',
|
|||
|
|
'station_name': '薛海气站',
|
|||
|
|
'staff_name': '张师傅',
|
|||
|
|
'staff_avatar': 'asset:assets/staff/delivery-worker.png',
|
|||
|
|
'staff_phone_masked': '138****5678',
|
|||
|
|
'controlled_call_available': false,
|
|||
|
|
'route_available': true,
|
|||
|
|
'destination_available': true,
|
|||
|
|
'destination_longitude': 116.472,
|
|||
|
|
'destination_latitude': 39.997,
|
|||
|
|
'points': [
|
|||
|
|
{'longitude': 116.451, 'latitude': 40.002, 'occurred_at': '2026-09-05T09:42:00+08:00'},
|
|||
|
|
{'longitude': 116.456, 'latitude': 40.001, 'occurred_at': '2026-09-05T09:47:00+08:00'},
|
|||
|
|
{'longitude': 116.461, 'latitude': 39.999, 'occurred_at': '2026-09-05T09:51:00+08:00'},
|
|||
|
|
{'longitude': 116.466, 'latitude': 39.998, 'occurred_at': '2026-09-05T09:56:00+08:00'},
|
|||
|
|
],
|
|||
|
|
'timeline': [
|
|||
|
|
{
|
|||
|
|
'status_code': 18,
|
|||
|
|
'title': '气站已接单',
|
|||
|
|
'detail': '薛海气站已接收您的订单',
|
|||
|
|
'occurred_at': '2026-09-05T09:21:00+08:00',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'status_code': 20,
|
|||
|
|
'title': '商品装车完成',
|
|||
|
|
'detail': '商品已装车,准备出发',
|
|||
|
|
'occurred_at': '2026-09-05T09:35:00+08:00',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'status_code': 33,
|
|||
|
|
'title': '配送员已出发',
|
|||
|
|
'detail': '配送员已从薛海气站出发',
|
|||
|
|
'occurred_at': '2026-09-05T09:42:00+08:00',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<RechargeOptions> rechargeOptions() async => const RechargeOptions(
|
|||
|
|
min: 1,
|
|||
|
|
max: 500000,
|
|||
|
|
channels: [
|
|||
|
|
RechargeChannel('wechat', app: true, wap: true),
|
|||
|
|
RechargeChannel('alipay', app: true, wap: true),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<GasOrderCheckoutData> gasOrderOptions() async => GasOrderCheckoutData(
|
|||
|
|
stationName: '薛海气站',
|
|||
|
|
stationStatus: '营业中',
|
|||
|
|
deliveryScope: '薛海镇及周边区域',
|
|||
|
|
deliveryFee: 0,
|
|||
|
|
products: const [
|
|||
|
|
GasOrderProductOption(
|
|||
|
|
name: '5kg 便携瓶',
|
|||
|
|
description: '小巧轻便,适合单人或短期使用',
|
|||
|
|
unitPrice: 5800,
|
|||
|
|
depositAmount: 10000,
|
|||
|
|
itemIdentities: ['g5'],
|
|||
|
|
),
|
|||
|
|
GasOrderProductOption(
|
|||
|
|
name: '10kg 家用瓶',
|
|||
|
|
description: '家庭日常使用,经济实惠',
|
|||
|
|
unitPrice: 9600,
|
|||
|
|
depositAmount: 10000,
|
|||
|
|
itemIdentities: ['g10'],
|
|||
|
|
),
|
|||
|
|
GasOrderProductOption(
|
|||
|
|
name: '15kg 家用瓶',
|
|||
|
|
description: '大容量更耐用,适合多人家庭',
|
|||
|
|
unitPrice: 12800,
|
|||
|
|
depositAmount: 10000,
|
|||
|
|
itemIdentities: ['g15'],
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
addresses: const [
|
|||
|
|
ShippingAddress(
|
|||
|
|
identity: 'a1',
|
|||
|
|
address: '薛海镇泰山路188号 薛海花园3栋602室',
|
|||
|
|
contactName: '张先生',
|
|||
|
|
contactPhone: '13812345678',
|
|||
|
|
isDefault: true,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
slots: [
|
|||
|
|
GasOrderAppointmentSlot(startAt: DateTime(2026, 9, 12, 9), label: '明日 09:00-11:00'),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<GasOrderCreateResult> createGasOrder({
|
|||
|
|
required String requestNo,
|
|||
|
|
required String addressIdentity,
|
|||
|
|
required List<String> itemIdentities,
|
|||
|
|
required DateTime appointmentAt,
|
|||
|
|
required int expectedAmount,
|
|||
|
|
}) async {
|
|||
|
|
gasOrderCreateCalls++;
|
|||
|
|
return GasOrderCreateResult(identity: 'gas-created', payableAmount: expectedAmount);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<DepositSummary> deposits() async => DepositSummary(
|
|||
|
|
refundableAmount: 20000,
|
|||
|
|
usingCount: 2,
|
|||
|
|
ruleText: '气瓶完好、回收检测合格后,押金原路退回至余额账户。',
|
|||
|
|
items: [
|
|||
|
|
DepositRecord(
|
|||
|
|
identity: 'd1',
|
|||
|
|
depositNo: 'YJ20260905001',
|
|||
|
|
status: 10,
|
|||
|
|
statusName: '使用中',
|
|||
|
|
amount: 10000,
|
|||
|
|
productName: '15kg 液化气钢瓶',
|
|||
|
|
productCode: 'GZ-15-0286',
|
|||
|
|
paidAt: DateTime(2026, 8, 18),
|
|||
|
|
refundedAt: null,
|
|||
|
|
allowedActions: const ['return_bottle'],
|
|||
|
|
),
|
|||
|
|
DepositRecord(
|
|||
|
|
identity: 'd2',
|
|||
|
|
depositNo: 'YJ20260904002',
|
|||
|
|
status: 20,
|
|||
|
|
statusName: '退款中',
|
|||
|
|
amount: 10000,
|
|||
|
|
productName: '15kg 液化气钢瓶',
|
|||
|
|
productCode: 'GZ-15-0193',
|
|||
|
|
paidAt: DateTime(2026, 8, 12),
|
|||
|
|
refundedAt: null,
|
|||
|
|
allowedActions: const [],
|
|||
|
|
),
|
|||
|
|
DepositRecord(
|
|||
|
|
identity: 'd3',
|
|||
|
|
depositNo: 'YJ20260828003',
|
|||
|
|
status: 23,
|
|||
|
|
statusName: '已退回',
|
|||
|
|
amount: 10000,
|
|||
|
|
productName: '15kg 液化气钢瓶',
|
|||
|
|
productCode: 'GZ-15-0127',
|
|||
|
|
paidAt: DateTime(2026, 7, 28),
|
|||
|
|
refundedAt: DateTime(2026, 7, 30),
|
|||
|
|
allowedActions: const [],
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
@override
|
|||
|
|
Future<DepositReturnRequest> createDepositReturn({
|
|||
|
|
required String depositIdentity,
|
|||
|
|
required ShippingAddress address,
|
|||
|
|
required DateTime appointmentStart,
|
|||
|
|
required DateTime appointmentEnd,
|
|||
|
|
required String requestNo,
|
|||
|
|
}) async {
|
|||
|
|
depositReturnCalls++;
|
|||
|
|
return const DepositReturnRequest(
|
|||
|
|
identity: 'return-1',
|
|||
|
|
status: 10,
|
|||
|
|
statusName: '待上门',
|
|||
|
|
estimatedAmount: 10000,
|
|||
|
|
refundAmount: 0,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<WalletBillPage> walletBills({String direction = '', String cursor = ''}) async {
|
|||
|
|
if (fail) throw const ApiException(500, '测试账单错误');
|
|||
|
|
final items = [
|
|||
|
|
for (final (id, type, amount, flow) in [
|
|||
|
|
('1', 'gas_order', 22800, 'expense'),
|
|||
|
|
('2', 'refund', 5900, 'income'),
|
|||
|
|
('3', 'recharge', 30000, 'income'),
|
|||
|
|
])
|
|||
|
|
WalletBill(
|
|||
|
|
identity: id,
|
|||
|
|
number: 'TEST-$id',
|
|||
|
|
direction: flow,
|
|||
|
|
tradeType: type,
|
|||
|
|
amount: amount,
|
|||
|
|
fee: 0,
|
|||
|
|
balanceAfter: 56800,
|
|||
|
|
createdAt: DateTime(2026, 9, 6 - int.parse(id)),
|
|||
|
|
channel: 'wallet',
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
return WalletBillPage(
|
|||
|
|
items.where((i) => direction.isEmpty || i.direction == direction).toList(),
|
|||
|
|
'',
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<GasOrderDetail> gasOrderDetail(String identity) async {
|
|||
|
|
final payment = identity == 'payment-1';
|
|||
|
|
return GasOrderDetail.fromJson({
|
|||
|
|
'identity': identity,
|
|||
|
|
'order_no': payment ? '202609051628001' : 'G202609050920001',
|
|||
|
|
'status_name': payment ? '待付款' : '待签收',
|
|||
|
|
'allowed_actions': payment ? ['pay', 'cancel'] : ['confirm_receipt'],
|
|||
|
|
'station_name': '薛海气站',
|
|||
|
|
'delivery_staff_name': '李师傅',
|
|||
|
|
'contract_identity': 'contract-1',
|
|||
|
|
'contract_no': 'HT20260905001',
|
|||
|
|
'product_amount': 12800,
|
|||
|
|
'deposit_amount': payment ? 10000 : 0,
|
|||
|
|
'delivery_fee': 0,
|
|||
|
|
'discount_amount': 0,
|
|||
|
|
'payable_amount': payment ? 22800 : 12800,
|
|||
|
|
'contact_name': '张女士',
|
|||
|
|
'contact_phone': '13800005678',
|
|||
|
|
'address': '北京市朝阳区望京街道望京中环南路2号星源国际B座1201室',
|
|||
|
|
'created_at': '2026-09-05T09:20:00+08:00',
|
|||
|
|
'items': [
|
|||
|
|
{'identity': 'gas-item', 'name': '15kg家用液化气', 'quantity': 1, 'sale_amount': 12800},
|
|||
|
|
],
|
|||
|
|
'timeline': payment
|
|||
|
|
? []
|
|||
|
|
: [
|
|||
|
|
{'status_name': '已分配', 'occurred_at': '2026-09-05T09:21:00+08:00'},
|
|||
|
|
{'status_name': '配送中', 'occurred_at': '2026-09-05T09:30:00+08:00'},
|
|||
|
|
{'status_name': '待签收', 'occurred_at': '2026-09-05T10:30:00+08:00'},
|
|||
|
|
],
|
|||
|
|
'payments': payment
|
|||
|
|
? []
|
|||
|
|
: [
|
|||
|
|
{'amount': 12800, 'channel': 'wechat', 'paid_at': '2026-09-05T09:22:00+08:00'},
|
|||
|
|
],
|
|||
|
|
}, (s) => s);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<List<GasContractSummary>> gasContracts() async => [
|
|||
|
|
GasContractSummary.fromJson({
|
|||
|
|
'identity': 'contract-1',
|
|||
|
|
'contract_no': 'HT20260905001',
|
|||
|
|
'title': '居民瓶装燃气供用气合同',
|
|||
|
|
'terms': '测试合同正文',
|
|||
|
|
'contract_status': 11,
|
|||
|
|
'station_name': '薛海气站',
|
|||
|
|
'signed_at': '2026-08-18T00:00:00+08:00',
|
|||
|
|
'effective_at': '2026-08-18T00:00:00+08:00',
|
|||
|
|
'expired_at': '2027-08-17T00:00:00+08:00',
|
|||
|
|
'has_attachment': false,
|
|||
|
|
}),
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<GasContractDetail> gasContractDetail(String identity) async => GasContractDetail.fromJson({
|
|||
|
|
'identity': identity,
|
|||
|
|
'contract_no': 'HT20260905001',
|
|||
|
|
'title': '居民瓶装燃气供用气合同',
|
|||
|
|
'terms': '测试合同正文,仅用于自动验证,不作为真实合同。',
|
|||
|
|
'contract_status': 11,
|
|||
|
|
'signed_at': '2026-08-18T00:00:00+08:00',
|
|||
|
|
'effective_at': '2026-08-18T00:00:00+08:00',
|
|||
|
|
'expired_at': '2027-08-17T00:00:00+08:00',
|
|||
|
|
'has_attachment': false,
|
|||
|
|
});
|
|||
|
|
@override
|
|||
|
|
Future<ShopOrderDetail> shopOrderDetail(String identity) async {
|
|||
|
|
final payment = identity == 'payment-1';
|
|||
|
|
return ShopOrderDetail.fromJson({
|
|||
|
|
'identity': identity,
|
|||
|
|
'order_no': payment ? 'EC202609051628001' : 'EC202609050920001',
|
|||
|
|
'status_name': payment ? '待付款' : '待收货',
|
|||
|
|
'allowed_actions': payment ? ['pay', 'cancel'] : ['refund', 'confirm_receipt'],
|
|||
|
|
'contact_name': '张女士',
|
|||
|
|
'contact_phone': '13800005678',
|
|||
|
|
'address': '北京市朝阳区望京街道望京中环南路2号星源国际B座1201室',
|
|||
|
|
'product_amount': payment ? 23800 : 12800,
|
|||
|
|
'discount_amount': payment ? 1000 : 0,
|
|||
|
|
'payable_amount': payment ? 22800 : 12800,
|
|||
|
|
'created_at': '2026-09-05T09:20:00+08:00',
|
|||
|
|
'paid_at': payment ? null : '2026-09-05T09:21:00+08:00',
|
|||
|
|
'shipped_at': payment ? null : '2026-09-05T09:30:00+08:00',
|
|||
|
|
'received_at': null,
|
|||
|
|
'remark': '送达前电话联系',
|
|||
|
|
'items': [
|
|||
|
|
{
|
|||
|
|
'identity': 'item-1',
|
|||
|
|
'product_identity': 'p1',
|
|||
|
|
'name': '15kg家用液化气',
|
|||
|
|
'quantity': 1,
|
|||
|
|
'sale_amount': payment ? 23800 : 12800,
|
|||
|
|
'image_url': '',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}, (s) => s);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<ProductRecommendations> recommendations({required String source, int page = 1}) async =>
|
|||
|
|
ProductRecommendations(
|
|||
|
|
items: const [
|
|||
|
|
ProductSummary(identity: 'r1', name: '家用燃气减压阀', price: 3900, stock: 10, category: '燃气配件'),
|
|||
|
|
ProductSummary(identity: 'r2', name: '不锈钢燃气管', price: 5900, stock: 10, category: '燃气配件'),
|
|||
|
|
],
|
|||
|
|
page: page,
|
|||
|
|
hasMore: false,
|
|||
|
|
);
|
|||
|
|
@override
|
|||
|
|
Future<ProductFavoriteState> favoriteState(String identity) async =>
|
|||
|
|
ProductFavoriteState(productIdentity: identity, active: false, revision: '');
|
|||
|
|
@override
|
|||
|
|
Future<List<ProductFavorite>> favorites() async => [
|
|||
|
|
for (final p in await products())
|
|||
|
|
ProductFavorite(
|
|||
|
|
state: ProductFavoriteState(productIdentity: p.identity, active: true, revision: 'r1'),
|
|||
|
|
product: ProductSummary(
|
|||
|
|
identity: p.identity,
|
|||
|
|
name: p.title,
|
|||
|
|
price: p.raw['price_amount'] as int,
|
|||
|
|
stock: 20,
|
|||
|
|
category: p.raw['category_name'] as String,
|
|||
|
|
),
|
|||
|
|
available: true,
|
|||
|
|
),
|
|||
|
|
const ProductFavorite(
|
|||
|
|
state: ProductFavoriteState(productIdentity: 'p5', active: true, revision: 'r1'),
|
|||
|
|
product: ProductSummary(
|
|||
|
|
identity: 'p5',
|
|||
|
|
name: '家用燃气灶 双灶',
|
|||
|
|
price: 69900,
|
|||
|
|
stock: 0,
|
|||
|
|
category: '灶具',
|
|||
|
|
),
|
|||
|
|
available: false,
|
|||
|
|
specifications: ['天然气'],
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<List<CartItem>> cart() async => const [
|
|||
|
|
CartItem(
|
|||
|
|
product: ProductSummary(identity: 'p1', name: '15kg家用液化气', price: 12800, stock: 30),
|
|||
|
|
quantity: 1,
|
|||
|
|
selected: true,
|
|||
|
|
available: true,
|
|||
|
|
revision: 'r1',
|
|||
|
|
),
|
|||
|
|
CartItem(
|
|||
|
|
product: ProductSummary(identity: 'p2', name: '燃气报警器', price: 16900, stock: 20),
|
|||
|
|
quantity: 1,
|
|||
|
|
selected: true,
|
|||
|
|
available: true,
|
|||
|
|
revision: 'r2',
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<ProductDetail> productDetail(String identity) async => ProductDetail(
|
|||
|
|
identity: identity,
|
|||
|
|
name: '15kg家用液化气',
|
|||
|
|
price: 12800,
|
|||
|
|
stock: 30,
|
|||
|
|
category: '家用液化气',
|
|||
|
|
attributes: const [(name: '净重', value: '15kg'), (name: '适用家庭', value: '3—5人家庭日常烹饪使用')],
|
|||
|
|
);
|
|||
|
|
int relationCalls = 0;
|
|||
|
|
@override
|
|||
|
|
Future<List<ShippingAddress>> shippingAddresses() async => const [
|
|||
|
|
ShippingAddress(
|
|||
|
|
identity: 'a1',
|
|||
|
|
address: '广东省广州市白云区薛海路88号\n6栋502',
|
|||
|
|
contactName: '张先生',
|
|||
|
|
contactPhone: '13800005678',
|
|||
|
|
isDefault: true,
|
|||
|
|
),
|
|||
|
|
ShippingAddress(
|
|||
|
|
identity: 'a2',
|
|||
|
|
address: '广东省广州市白云区泰山路188号\n薛海花园3栋602',
|
|||
|
|
contactName: '李女士',
|
|||
|
|
contactPhone: '13900006078',
|
|||
|
|
),
|
|||
|
|
ShippingAddress(
|
|||
|
|
identity: 'a3',
|
|||
|
|
address: '广东省广州市白云区同和街道同泰路28号\n同泰大厦1栋901',
|
|||
|
|
contactName: '王先生',
|
|||
|
|
contactPhone: '13700008910',
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> contents() async {
|
|||
|
|
if (fail) throw const ApiException(500, '测试网络失败');
|
|||
|
|
return const [
|
|||
|
|
ClientRecord(
|
|||
|
|
identity: 'notice-1',
|
|||
|
|
title: '夏季用气安全提醒',
|
|||
|
|
subtitle: '',
|
|||
|
|
raw: {
|
|||
|
|
'body': '高温天气注意通风,定期检查燃气设备',
|
|||
|
|
'content_type': 'notice',
|
|||
|
|
'version_no': 1,
|
|||
|
|
'created_at': '2026-07-26T10:00:00+08:00',
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
ClientRecord(
|
|||
|
|
identity: 'notice-2',
|
|||
|
|
title: '燃气设施维护通知',
|
|||
|
|
subtitle: '',
|
|||
|
|
raw: {
|
|||
|
|
'body': '本周将对部分区域进行例行维护',
|
|||
|
|
'content_type': 'notice',
|
|||
|
|
'version_no': 1,
|
|||
|
|
'created_at': '2026-07-24T10:00:00+08:00',
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<Map<String, Object?>?> serviceRelation() async {
|
|||
|
|
relationCalls++;
|
|||
|
|
return {'gas_name': '薛海气站', 'delivery_name': '城南配送点'};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> products() async => [
|
|||
|
|
for (final (identity, name, price, category) in [
|
|||
|
|
('p1', '15kg家用液化气', 12800, '液化气'),
|
|||
|
|
('p2', '燃气报警器', 16900, '安全设备'),
|
|||
|
|
('p3', '不锈钢减压阀', 5900, '燃气配件'),
|
|||
|
|
('p4', '燃气软管2米', 2990, '燃气配件'),
|
|||
|
|
])
|
|||
|
|
ClientRecord(
|
|||
|
|
identity: identity,
|
|||
|
|
title: name,
|
|||
|
|
subtitle: '',
|
|||
|
|
raw: {'price_amount': price, 'stock_quantity': 20, 'category_name': category},
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<UserProfile> profile() async => const UserProfile(
|
|||
|
|
identity: 'user-fixture',
|
|||
|
|
name: '张先生',
|
|||
|
|
phone: '13800005678',
|
|||
|
|
avatar: '',
|
|||
|
|
realName: '张先生',
|
|||
|
|
);
|
|||
|
|
@override
|
|||
|
|
Future<WalletSummary> wallet() async =>
|
|||
|
|
const WalletSummary(balance: 56800, withdrawalBalance: 56800, paymentPasswordSet: true);
|
|||
|
|
@override
|
|||
|
|
Future<List<WalletBank>> walletBanks() async => const [
|
|||
|
|
WalletBank(
|
|||
|
|
identity: 'bank-1',
|
|||
|
|
maskedNumber: '**** **** **** 2868',
|
|||
|
|
bankName: '中国建设银行',
|
|||
|
|
owner: '张先生',
|
|||
|
|
type: 'debit',
|
|||
|
|
isDefault: true,
|
|||
|
|
),
|
|||
|
|
WalletBank(
|
|||
|
|
identity: 'bank-2',
|
|||
|
|
maskedNumber: '**** **** **** 6078',
|
|||
|
|
bankName: '中国工商银行',
|
|||
|
|
owner: '张先生',
|
|||
|
|
type: 'debit',
|
|||
|
|
isDefault: false,
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<List<WalletWithdrawal>> walletWithdrawals() async => const [];
|
|||
|
|
@override
|
|||
|
|
Future<WalletWithdrawal> createWalletWithdrawal({
|
|||
|
|
required String bankIdentity,
|
|||
|
|
required int amount,
|
|||
|
|
required String requestNo,
|
|||
|
|
required String paymentPassword,
|
|||
|
|
}) async {
|
|||
|
|
withdrawalCalls++;
|
|||
|
|
return WalletWithdrawal(
|
|||
|
|
identity: 'withdraw-1',
|
|||
|
|
number: 'WD20260911001',
|
|||
|
|
amount: amount,
|
|||
|
|
fee: 0,
|
|||
|
|
status: 10,
|
|||
|
|
createdAt: DateTime(2026, 9, 11, 12),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> addresses() async => [];
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> emergencyContacts() async => const [
|
|||
|
|
ClientRecord(identity: 'contact-1', title: '家人一', subtitle: '', raw: {}),
|
|||
|
|
ClientRecord(identity: 'contact-2', title: '家人二', subtitle: '', raw: {}),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> shopOrders() async => const [
|
|||
|
|
ClientRecord(
|
|||
|
|
identity: 'order-1',
|
|||
|
|
title: '订单号:1785051399865',
|
|||
|
|
subtitle: '',
|
|||
|
|
status: 16,
|
|||
|
|
raw: {
|
|||
|
|
'payable_amount': 22800,
|
|||
|
|
'status_name': '待付款',
|
|||
|
|
'allowed_actions': ['pay', 'cancel'],
|
|||
|
|
'created_at': '2026-07-26T15:36:00+08:00',
|
|||
|
|
'items': [
|
|||
|
|
{'identity': 'item-1', 'quantity': 1, 'product_snapshot': '{"name":"15kg家用液化气"}'},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> gasOrders() async => (await shopOrders())
|
|||
|
|
.map(
|
|||
|
|
(r) => ClientRecord(
|
|||
|
|
identity: r.identity,
|
|||
|
|
title: r.title,
|
|||
|
|
subtitle: r.subtitle,
|
|||
|
|
status: r.status,
|
|||
|
|
raw: {
|
|||
|
|
...r.raw,
|
|||
|
|
'station_name': '薛海气站',
|
|||
|
|
'items': [
|
|||
|
|
{
|
|||
|
|
'identity': 'item-1',
|
|||
|
|
'quantity': 1,
|
|||
|
|
'product_type_name': '15kg家用液化气',
|
|||
|
|
'product_params': '{"weight":"15kg"}',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
'allowed_actions': ['pay', 'cancel'],
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
)
|
|||
|
|
.toList();
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> refunds() async => [];
|
|||
|
|
@override
|
|||
|
|
Future<List<ClientRecord>> tickets() async => [
|
|||
|
|
const ClientRecord(
|
|||
|
|
identity: 't1',
|
|||
|
|
title: 'BX202609040123',
|
|||
|
|
subtitle: '厨房角阀关闭后仍有轻微异响',
|
|||
|
|
status: 34,
|
|||
|
|
raw: {
|
|||
|
|
'ticket_no': 'BX202609040123',
|
|||
|
|
'ticket_status': 34,
|
|||
|
|
'status_name': '待确认',
|
|||
|
|
'allowed_actions': ['cancel', 'confirm'],
|
|||
|
|
'category': 'repair',
|
|||
|
|
'description': '厨房角阀关闭后仍有轻微异响',
|
|||
|
|
'address': '四川省成都市武侯区万科·公园传奇南区3栋2单元1804',
|
|||
|
|
'created_at': '2026-09-04T01:15:00Z',
|
|||
|
|
'started_at': '2026-09-04T08:00:00Z',
|
|||
|
|
'appointment_at': '2026-09-04T08:00:00Z',
|
|||
|
|
'result': '已检查阀门并更换密封件,请确认处理结果。',
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
}
|