Files
jinzhi/lib/features/settings/application/feedback_email_service.dart
T
2026-08-13 17:47:01 +08:00

156 lines
4.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../common/services/device_header_service.dart';
import '../../auth/data/auth_models.dart';
class FeedbackEmailService {
const FeedbackEmailService._();
static const recipient = 'business@yqzhice.top';
static const productName = '金值';
static Future<void> send(AuthState? authState) async {
final email = await _buildEmail(authState);
final uri = composeMailtoUri(
recipient: email.recipient,
subject: email.subject,
body: email.body,
);
try {
final launched = await launchUrl(
uri,
mode: LaunchMode.externalApplication,
);
if (!launched) throw const FeedbackEmailUnavailableException();
} catch (_) {
throw const FeedbackEmailUnavailableException();
}
}
@visibleForTesting
static Uri composeMailtoUri({
required String recipient,
required String subject,
required String body,
}) => Uri(
scheme: 'mailto',
path: recipient,
query: _encodeQueryParameters({'subject': subject, 'body': body}),
);
static String _encodeQueryParameters(
Map<String, String> parameters,
) => parameters.entries
.map(
(entry) =>
'${Uri.encodeComponent(entry.key)}=${Uri.encodeComponent(entry.value)}',
)
.join('&');
static Future<_FeedbackEmail> _buildEmail(AuthState? authState) async {
final packageInfo = await PackageInfo.fromPlatform();
final device = await _loadDeviceInfo();
try {
await DeviceHeaderService.to.ensureInitialized();
} catch (_) {}
final headers = DeviceHeaderService.to.commonHeaders();
final loggedIn = authState is LoggedInAuthState ? authState : null;
final version = '${packageInfo.version}+${packageInfo.buildNumber}';
final diagnostics = [
'App version: $version',
'Channel: ${headers['User-Channel'] ?? ''}',
'OS version: ${device.osVersion}',
'Device manufacturer: ${device.manufacturer}',
'Device model: ${device.model}',
'Login type: ${loggedIn == null ? 'guest' : 'phone'}',
'Phone: ${loggedIn?.user.phone ?? ''}',
].join('\n');
return _FeedbackEmail(
recipient: recipient,
subject:
'$productName反馈[$version]-[${device.manufacturer}]-[${device.osVersion}]',
body: [
'$productName团队你好,',
'请描述你的问题,可附上截图或视频:',
'',
'',
'----',
diagnostics,
].join('\n'),
);
}
static Future<_FeedbackDeviceInfo> _loadDeviceInfo() async {
final plugin = DeviceInfoPlugin();
try {
if (kIsWeb) {
final info = await plugin.webBrowserInfo;
return _FeedbackDeviceInfo(
osVersion: info.platform ?? 'web',
manufacturer: info.browserName.name,
model: info.userAgent ?? '',
);
}
if (defaultTargetPlatform == TargetPlatform.android) {
final info = await plugin.androidInfo;
return _FeedbackDeviceInfo(
osVersion: info.version.release,
manufacturer: info.manufacturer,
model: info.model,
);
}
if (defaultTargetPlatform == TargetPlatform.iOS) {
final info = await plugin.iosInfo;
return _FeedbackDeviceInfo(
osVersion: info.systemVersion,
manufacturer: 'Apple',
model: info.utsname.machine,
);
}
return const _FeedbackDeviceInfo(
osVersion: '',
manufacturer: '',
model: '',
);
} catch (_) {
return const _FeedbackDeviceInfo(
osVersion: '',
manufacturer: '',
model: '',
);
}
}
}
class FeedbackEmailUnavailableException implements Exception {
const FeedbackEmailUnavailableException();
}
class _FeedbackEmail {
const _FeedbackEmail({
required this.recipient,
required this.subject,
required this.body,
});
final String recipient;
final String subject;
final String body;
}
class _FeedbackDeviceInfo {
const _FeedbackDeviceInfo({
required this.osVersion,
required this.manufacturer,
required this.model,
});
final String osVersion;
final String manufacturer;
final String model;
}