😋 初始化仓库

This commit is contained in:
2025-11-13 09:14:49 +08:00
commit 347d264437
133 changed files with 11214 additions and 0 deletions

2
lib/utils/.gitkeep Normal file
View File

@@ -0,0 +1,2 @@
# Utils directory
# This directory contains utility functions and helpers

239
lib/utils/app_logger.dart Normal file
View File

@@ -0,0 +1,239 @@
import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';
/// Application-wide logger with configurable levels and output
class AppLogger {
static final AppLogger _instance = AppLogger._internal();
factory AppLogger() => _instance;
late final Logger _logger;
bool _isInitialized = false;
AppLogger._internal();
/// Initialize the logger with appropriate settings
void initialize({Level? level, LogOutput? output, LogFilter? filter}) {
if (_isInitialized) return;
_logger = Logger(
level: level ?? _getDefaultLevel(),
filter: filter ?? ProductionFilter(),
printer: _createPrinter(),
output: output ?? _createOutput(),
);
_isInitialized = true;
}
/// Get default log level based on build mode
Level _getDefaultLevel() {
if (kDebugMode) {
return Level.debug;
} else if (kProfileMode) {
return Level.info;
} else {
return Level.warning;
}
}
/// Create appropriate printer based on build mode
LogPrinter _createPrinter() {
if (kDebugMode) {
// Detailed printer for development
return PrettyPrinter(
methodCount: 2,
errorMethodCount: 8,
lineLength: 120,
colors: true,
printEmojis: true,
dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart,
);
} else {
// Simple printer for production
return SimplePrinter(colors: false);
}
}
/// Create appropriate output based on build mode
LogOutput _createOutput() {
if (kReleaseMode) {
// In production, you might want to send logs to a file or remote service
return ConsoleOutput();
} else {
return ConsoleOutput();
}
}
/// Ensure logger is initialized
void _ensureInitialized() {
if (!_isInitialized) {
initialize();
}
}
/// Log a trace message (most verbose)
void trace(String message, {Object? error, StackTrace? stackTrace}) {
_ensureInitialized();
_logger.t(message, error: error, stackTrace: stackTrace);
}
/// Log a debug message
void debug(String message, {Object? error, StackTrace? stackTrace}) {
_ensureInitialized();
_logger.d(message, error: error, stackTrace: stackTrace);
}
/// Log an info message
void info(String message, {Object? error, StackTrace? stackTrace}) {
_ensureInitialized();
_logger.i(message, error: error, stackTrace: stackTrace);
}
/// Log a warning message
void warning(String message, {Object? error, StackTrace? stackTrace}) {
_ensureInitialized();
_logger.w(message, error: error, stackTrace: stackTrace);
}
/// Log an error message
void error(String message, {Object? error, StackTrace? stackTrace}) {
_ensureInitialized();
_logger.e(message, error: error, stackTrace: stackTrace);
}
/// Log a fatal error message
void fatal(String message, {Object? error, StackTrace? stackTrace}) {
_ensureInitialized();
_logger.f(message, error: error, stackTrace: stackTrace);
}
/// Log network request
void logRequest(
String method,
String url, {
Map<String, dynamic>? headers,
dynamic body,
}) {
if (!kReleaseMode) {
_ensureInitialized();
final buffer = StringBuffer();
buffer.writeln('$method $url');
if (headers != null && headers.isNotEmpty) {
buffer.writeln('Headers: ${_sanitizeHeaders(headers)}');
}
if (body != null) {
buffer.writeln('Body: ${_sanitizeBody(body)}');
}
_logger.d(buffer.toString());
}
}
/// Log network response
void logResponse(
String method,
String url,
int statusCode, {
dynamic body,
Duration? duration,
}) {
if (!kReleaseMode) {
_ensureInitialized();
final buffer = StringBuffer();
buffer.write('$method $url [$statusCode]');
if (duration != null) {
buffer.write(' (${duration.inMilliseconds}ms)');
}
buffer.writeln();
if (body != null) {
buffer.writeln('Body: ${_sanitizeBody(body)}');
}
if (statusCode >= 200 && statusCode < 300) {
_logger.d(buffer.toString());
} else if (statusCode >= 400) {
_logger.e(buffer.toString());
} else {
_logger.w(buffer.toString());
}
}
}
/// Sanitize headers to remove sensitive information
Map<String, dynamic> _sanitizeHeaders(Map<String, dynamic> headers) {
final sanitized = Map<String, dynamic>.from(headers);
// List of sensitive header keys to redact
const sensitiveKeys = [
'authorization',
'cookie',
'set-cookie',
'x-api-key',
'x-auth-token',
];
for (final key in sensitiveKeys) {
if (sanitized.containsKey(key.toLowerCase())) {
sanitized[key] = '***REDACTED***';
}
}
return sanitized;
}
/// Sanitize body to remove sensitive information
dynamic _sanitizeBody(dynamic body) {
if (body is Map) {
final sanitized = Map<String, dynamic>.from(body);
// List of sensitive field names to redact
const sensitiveFields = [
'password',
'pwd',
'passwd',
'token',
'secret',
'apiKey',
'api_key',
];
for (final field in sensitiveFields) {
if (sanitized.containsKey(field)) {
sanitized[field] = '***REDACTED***';
}
}
return sanitized;
}
// For string bodies, check if it contains sensitive patterns
if (body is String) {
if (body.length > 1000) {
return '${body.substring(0, 1000)}... (truncated)';
}
// Redact potential passwords in query strings or form data
return body.replaceAllMapped(
RegExp(
r'(password|pwd|passwd|token|secret)=[^&\s]+',
caseSensitive: false,
),
(match) => '${match.group(1)}=***REDACTED***',
);
}
return body;
}
/// Close the logger and release resources
void close() {
if (_isInitialized) {
_logger.close();
_isInitialized = false;
}
}
}

View File

@@ -0,0 +1,174 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'app_logger.dart';
import 'exceptions.dart';
/// Global error handler for the application
class ErrorHandler {
static final ErrorHandler _instance = ErrorHandler._internal();
factory ErrorHandler() => _instance;
ErrorHandler._internal();
final AppLogger _logger = AppLogger();
bool _isInitialized = false;
/// Initialize global error handling
void initialize() {
if (_isInitialized) return;
// Capture Flutter framework errors
FlutterError.onError = (FlutterErrorDetails details) {
_handleFlutterError(details);
};
// Capture errors in platform-specific code
PlatformDispatcher.instance.onError = (error, stack) {
_handlePlatformError(error, stack);
return true;
};
_isInitialized = true;
_logger.info('Global error handler initialized');
}
/// Run the app with error zone guarding
static Future<void> runAppWithErrorHandling(
Future<void> Function() appRunner,
) async {
final errorHandler = ErrorHandler();
errorHandler.initialize();
// Run app in a guarded zone to catch async errors
await runZonedGuarded(
() async {
await appRunner();
},
(error, stackTrace) {
errorHandler._handleZoneError(error, stackTrace);
},
);
}
/// Handle Flutter framework errors
void _handleFlutterError(FlutterErrorDetails details) {
// Log the error
_logger.error(
'Flutter Error',
error: details.exception,
stackTrace: details.stack,
);
// In debug mode, show the red error screen
if (kDebugMode) {
FlutterError.presentError(details);
}
// Optionally report to crash reporting service
_reportError(details.exception, details.stack);
}
/// Handle platform-specific errors
bool _handlePlatformError(Object error, StackTrace stackTrace) {
_logger.error('Platform Error', error: error, stackTrace: stackTrace);
_reportError(error, stackTrace);
return true;
}
/// Handle errors from runZonedGuarded
void _handleZoneError(Object error, StackTrace stackTrace) {
_logger.error('Async Error', error: error, stackTrace: stackTrace);
_reportError(error, stackTrace);
}
/// Report error to external service (optional)
void _reportError(Object error, StackTrace? stackTrace) {
// In production, you could send errors to services like:
// - Firebase Crashlytics
// - Sentry
// - Custom error reporting endpoint
if (kReleaseMode) {
// TODO: Implement error reporting to external service
// Example: FirebaseCrashlytics.instance.recordError(error, stackTrace);
}
}
/// Handle and display user-friendly error messages
static String getUserFriendlyMessage(Object error) {
if (error is AppException) {
return error.message;
} else if (error is NetworkException) {
return '网络连接失败,请检查网络设置';
} else if (error is AuthenticationException) {
return '登录失败,请检查账号密码';
} else if (error is ParseException) {
return '数据解析失败,请稍后重试';
} else if (error is ValidationException) {
return '输入验证失败:${error.message}';
} else if (error is TimeoutException) {
return '请求超时,请稍后重试';
} else if (error is FormatException) {
return '数据格式错误';
} else {
return '发生未知错误,请稍后重试';
}
}
/// Show error dialog to user
static void showErrorDialog(
BuildContext context,
Object error, {
VoidCallback? onRetry,
}) {
final message = getUserFriendlyMessage(error);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('错误'),
content: Text(message),
actions: [
if (onRetry != null)
TextButton(
onPressed: () {
Navigator.of(context).pop();
onRetry();
},
child: const Text('重试'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('确定'),
),
],
),
);
}
/// Show error snackbar to user
static void showErrorSnackBar(
BuildContext context,
Object error, {
Duration duration = const Duration(seconds: 3),
}) {
final message = getUserFriendlyMessage(error);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: duration,
backgroundColor: Colors.red,
action: SnackBarAction(
label: '关闭',
textColor: Colors.white,
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
),
);
}
}

148
lib/utils/exceptions.dart Normal file
View File

@@ -0,0 +1,148 @@
/// Base exception class for all application exceptions
abstract class AppException implements Exception {
final String message;
final String? details;
final StackTrace? stackTrace;
AppException(this.message, [this.details, this.stackTrace]);
@override
String toString() {
if (details != null) {
return '$runtimeType: $message\nDetails: $details';
}
return '$runtimeType: $message';
}
}
/// Exception thrown when network operations fail
class NetworkException extends AppException {
final int? statusCode;
final String? url;
NetworkException(
super.message, [
super.details,
super.stackTrace,
this.statusCode,
this.url,
]);
@override
String toString() {
final buffer = StringBuffer('NetworkException: $message');
if (statusCode != null) {
buffer.write('\nStatus Code: $statusCode');
}
if (url != null) {
buffer.write('\nURL: $url');
}
if (details != null) {
buffer.write('\nDetails: $details');
}
return buffer.toString();
}
}
/// Exception thrown when authentication fails
class AuthenticationException extends AppException {
final String? userId;
final AuthenticationFailureReason? reason;
AuthenticationException(
super.message, [
super.details,
super.stackTrace,
this.userId,
this.reason,
]);
@override
String toString() {
final buffer = StringBuffer('AuthenticationException: $message');
if (userId != null) {
buffer.write('\nUser ID: $userId');
}
if (reason != null) {
buffer.write('\nReason: ${reason!.name}');
}
if (details != null) {
buffer.write('\nDetails: $details');
}
return buffer.toString();
}
}
/// Reasons for authentication failure
enum AuthenticationFailureReason {
invalidCredentials,
sessionExpired,
networkError,
serverError,
unknown,
}
/// Exception thrown when parsing fails
class ParseException extends AppException {
final String? source;
final String? expectedFormat;
ParseException(
super.message, [
super.details,
super.stackTrace,
this.source,
this.expectedFormat,
]);
@override
String toString() {
final buffer = StringBuffer('ParseException: $message');
if (expectedFormat != null) {
buffer.write('\nExpected Format: $expectedFormat');
}
if (source != null && source!.length <= 100) {
buffer.write('\nSource: $source');
} else if (source != null) {
buffer.write('\nSource: ${source!.substring(0, 100)}...');
}
if (details != null) {
buffer.write('\nDetails: $details');
}
return buffer.toString();
}
}
/// Exception thrown when validation fails
class ValidationException extends AppException {
final String? fieldName;
final dynamic invalidValue;
final List<String>? validationRules;
ValidationException(
super.message, [
super.details,
super.stackTrace,
this.fieldName,
this.invalidValue,
this.validationRules,
]);
@override
String toString() {
final buffer = StringBuffer('ValidationException: $message');
if (fieldName != null) {
buffer.write('\nField: $fieldName');
}
if (invalidValue != null) {
buffer.write('\nInvalid Value: $invalidValue');
}
if (validationRules != null && validationRules!.isNotEmpty) {
buffer.write('\nValidation Rules: ${validationRules!.join(", ")}');
}
if (details != null) {
buffer.write('\nDetails: $details');
}
return buffer.toString();
}
}

View File

@@ -0,0 +1,76 @@
import 'dart:math';
/// 重试处理器,支持指数退避策略
class RetryHandler {
static const int maxRetries = 3;
static const Duration initialDelay = Duration(seconds: 1);
static const double exponentialBase = 2.0;
/// 执行带重试的异步操作
///
/// [operation] 要执行的异步操作
/// [retryIf] 可选的条件函数返回true时才重试
/// [maxAttempts] 最大尝试次数默认为3次
/// [onRetry] 可选的重试回调,参数为当前尝试次数和错误
static Future<T> retry<T>({
required Future<T> Function() operation,
bool Function(dynamic error)? retryIf,
int maxAttempts = maxRetries,
void Function(int attempt, dynamic error)? onRetry,
}) async {
int attempt = 0;
dynamic lastError;
while (true) {
try {
attempt++;
return await operation();
} catch (e) {
lastError = e;
// 检查是否应该重试
if (attempt >= maxAttempts || (retryIf != null && !retryIf(e))) {
rethrow;
}
// 计算延迟时间(指数退避)
final delay = initialDelay * pow(exponentialBase, attempt - 1);
// 调用重试回调
if (onRetry != null) {
onRetry(attempt, e);
}
// 等待后重试
await Future.delayed(delay);
}
}
}
/// 判断错误是否应该重试(网络相关错误)
static bool shouldRetryOnError(dynamic error) {
// 可以根据具体错误类型判断是否应该重试
// 例如:网络超时、连接失败等应该重试
// 认证失败、参数错误等不应该重试
final errorStr = error.toString().toLowerCase();
// 应该重试的错误类型
if (errorStr.contains('timeout') ||
errorStr.contains('connection') ||
errorStr.contains('network') ||
errorStr.contains('socket')) {
return true;
}
// 不应该重试的错误类型
if (errorStr.contains('authentication') ||
errorStr.contains('unauthorized') ||
errorStr.contains('forbidden') ||
errorStr.contains('invalid')) {
return false;
}
// 默认重试
return true;
}
}

View File

@@ -0,0 +1,263 @@
import 'package:flutter/foundation.dart';
import '../models/user_credentials.dart';
import '../providers/auth_provider.dart';
/// Session manager for handling app startup and session restoration
///
/// Provides utilities for:
/// - Checking if saved credentials exist
/// - Attempting to restore previous session
/// - Handling session expiration
///
/// Usage example:
/// ```dart
/// final sessionManager = SessionManager(authProvider: authProvider);
///
/// // Check if session can be restored
/// final canRestore = await sessionManager.canRestoreSession();
///
/// // Attempt to restore session
/// final restored = await sessionManager.restoreSession();
///
/// // Handle session expiration
/// await sessionManager.handleSessionExpired();
/// ```
class SessionManager {
final AuthProvider _authProvider;
SessionManager({required AuthProvider authProvider})
: _authProvider = authProvider;
/// Check if saved credentials exist
///
/// Returns true if credentials are stored, false otherwise
/// Does not validate if the session is still active
Future<bool> hasSavedCredentials() async {
try {
final credentials = await UserCredentials.loadSecurely();
return credentials != null;
} catch (e) {
debugPrint('Error checking saved credentials: $e');
return false;
}
}
/// Check if session can be restored
///
/// Checks if saved credentials exist and are valid
/// Returns true if session restoration should be attempted
Future<bool> canRestoreSession() async {
return await hasSavedCredentials();
}
/// Attempt to restore session from saved credentials
///
/// Loads credentials from secure storage and attempts login
/// Returns SessionRestoreResult with status and details
Future<SessionRestoreResult> restoreSession() async {
try {
// Check if credentials exist
final hasCredentials = await hasSavedCredentials();
if (!hasCredentials) {
return SessionRestoreResult(
success: false,
reason: SessionRestoreFailureReason.noCredentials,
message: '未找到保存的登录凭证',
);
}
// Attempt to restore session using AuthProvider
final restored = await _authProvider.restoreSession();
if (restored) {
return SessionRestoreResult(success: true, message: '会话恢复成功');
} else {
// Check the error message from auth provider
final errorMessage = _authProvider.errorMessage;
final reason = _determineFailureReason(errorMessage);
return SessionRestoreResult(
success: false,
reason: reason,
message: errorMessage ?? '会话恢复失败',
);
}
} catch (e) {
debugPrint('Error restoring session: $e');
return SessionRestoreResult(
success: false,
reason: SessionRestoreFailureReason.unknown,
message: '会话恢复出错: $e',
);
}
}
/// Handle session expiration
///
/// Clears current session and credentials
/// Should be called when session is detected as expired
Future<void> handleSessionExpired() async {
try {
await _authProvider.logout();
debugPrint('Session expired and cleared');
} catch (e) {
debugPrint('Error handling session expiration: $e');
}
}
/// Clear saved session data
///
/// Removes all saved credentials and session information
/// Useful for logout or when user wants to clear data
Future<void> clearSession() async {
try {
await _authProvider.logout();
debugPrint('Session cleared successfully');
} catch (e) {
debugPrint('Error clearing session: $e');
}
}
/// Validate current session
///
/// Checks if the current session is still valid
/// Returns true if session is active and healthy
Future<bool> validateSession() async {
try {
if (!_authProvider.isAuthenticated) {
return false;
}
return await _authProvider.checkSession();
} catch (e) {
debugPrint('Error validating session: $e');
return false;
}
}
/// Get session status
///
/// Returns current session status information
SessionStatus getSessionStatus() {
return SessionStatus(
isAuthenticated: _authProvider.isAuthenticated,
authState: _authProvider.state,
hasConnection: _authProvider.connection != null,
errorMessage: _authProvider.errorMessage,
);
}
/// Determine failure reason from error message
SessionRestoreFailureReason _determineFailureReason(String? errorMessage) {
if (errorMessage == null) {
return SessionRestoreFailureReason.unknown;
}
if (errorMessage.contains('密码错误') || errorMessage.contains('凭证')) {
return SessionRestoreFailureReason.invalidCredentials;
} else if (errorMessage.contains('网络') || errorMessage.contains('连接')) {
return SessionRestoreFailureReason.networkError;
} else if (errorMessage.contains('过期')) {
return SessionRestoreFailureReason.sessionExpired;
} else {
return SessionRestoreFailureReason.unknown;
}
}
}
/// Result of session restoration attempt
class SessionRestoreResult {
final bool success;
final SessionRestoreFailureReason? reason;
final String message;
SessionRestoreResult({
required this.success,
this.reason,
required this.message,
});
@override
String toString() {
return 'SessionRestoreResult(success: $success, reason: $reason, message: $message)';
}
}
/// Reasons why session restoration might fail
enum SessionRestoreFailureReason {
/// No saved credentials found
noCredentials,
/// Saved credentials are invalid
invalidCredentials,
/// Session has expired
sessionExpired,
/// Network connection error
networkError,
/// Unknown error
unknown,
}
/// Current session status information
class SessionStatus {
final bool isAuthenticated;
final AuthState authState;
final bool hasConnection;
final String? errorMessage;
SessionStatus({
required this.isAuthenticated,
required this.authState,
required this.hasConnection,
this.errorMessage,
});
/// Check if session is healthy
bool get isHealthy =>
isAuthenticated && hasConnection && errorMessage == null;
@override
String toString() {
return 'SessionStatus('
'isAuthenticated: $isAuthenticated, '
'authState: $authState, '
'hasConnection: $hasConnection, '
'errorMessage: $errorMessage'
')';
}
}
/// Extension methods for SessionRestoreFailureReason
extension SessionRestoreFailureReasonExtension on SessionRestoreFailureReason {
/// Get user-friendly message for the failure reason
String get userMessage {
switch (this) {
case SessionRestoreFailureReason.noCredentials:
return '未找到保存的登录信息,请重新登录';
case SessionRestoreFailureReason.invalidCredentials:
return '登录凭证无效,请重新登录';
case SessionRestoreFailureReason.sessionExpired:
return '会话已过期,请重新登录';
case SessionRestoreFailureReason.networkError:
return '网络连接失败,请检查网络后重试';
case SessionRestoreFailureReason.unknown:
return '会话恢复失败,请重新登录';
}
}
/// Check if user should be prompted to login again
bool get shouldPromptLogin {
switch (this) {
case SessionRestoreFailureReason.noCredentials:
case SessionRestoreFailureReason.invalidCredentials:
case SessionRestoreFailureReason.sessionExpired:
return true;
case SessionRestoreFailureReason.networkError:
case SessionRestoreFailureReason.unknown:
return false; // User might want to retry
}
}
}

View File

@@ -0,0 +1,194 @@
import 'dart:math';
import '../models/questionnaire.dart';
/// Text generator for evaluation questionnaires
/// Generates appropriate text responses based on question type
class TextGenerator {
static final Random _random = Random();
// 启发类文案库
static const List<String> inspirationTexts = [
"老师认真负责的态度和丰富的讲课内容,让我明白了扎实的知识积累对学习的重要性",
"老师能够深入了解学生的学习状况,启发我学会了关注细节、因材施教的道理",
"老师授课有条理有重点,教会我做事要分清主次、抓住关键的思维方法",
"老师善于用凝练的语言表达复杂内容,让我学会了如何提炼要点、化繁为简",
"老师对深奥现象解释得通俗易懂,启发我认识到深入浅出是一种重要的能力",
"老师采用多种教学方式让学生更好接受知识,让我明白了方法灵活运用的重要性",
"老师既严格要求又鼓励学生发言,教会我严慈相济、宽严并济的处事原则",
"老师能够调动学生的积极性,启发我懂得了激发他人潜能和主动性的价值",
"老师课堂气氛活跃但不失严谨,让我理解了轻松与高效可以兼得的道理",
"老师治学严谨、循循善诱的风格,激励我要保持谦逊认真的学习态度和钻研精神",
"老师对学科的热爱和投入,让我感受到保持热情对做好任何事情的重要意义",
"老师善于联系实际讲解理论知识,启发我学会了理论联系实际的思维方式",
"老师注重培养学生的自主学习能力,让我明白了授人以渔的教育真谛",
"老师对每个问题的耐心解答,教会我做事要有耐心和责任心",
"老师在课堂上的幽默感,让我懂得了适度的轻松能够提高工作和学习效率",
"老师严格的课堂管理,启发我认识到纪律和规则对集体活动的重要性",
"老师丰富的专业知识储备,激励我要不断充实自己、拓宽知识面",
"老师对学生的一视同仁,让我理解了公平公正待人的重要价值",
"老师善于鼓励和肯定学生,教会我正面激励对他人成长的积极作用",
"老师清晰的逻辑思维,启发我学会了有条理地思考和表达问题",
"老师对教学的精心准备,让我明白了充分准备是做好工作的前提",
"老师善于归纳总结重点,教会我抓住事物本质和核心的思维能力",
"老师对学生问题的重视,启发我懂得了倾听和尊重他人意见的重要性",
"老师灵活的教学节奏把握,让我学会了根据实际情况灵活调整的智慧",
"老师富有感染力的授课方式,教会我热情和真诚能够打动他人",
"老师注重学生的全面发展,启发我认识到综合素质培养的重要性",
"老师对细节的关注,让我明白了细节决定成败的道理",
"老师善于启发学生独立思考,教会我批判性思维和质疑精神的可贵",
"老师持续学习、与时俱进的态度,激励我要保持终身学习的理念",
"老师对学生的关心和帮助,让我理解了教书育人、为人师表的深刻内涵",
];
// 建议类文案库
static const List<String> suggestionTexts = [
'',
'没有',
"老师讲课很好,很认真负责,我没有什么建议,希望老师继续保持现有的教学方式",
"老师授课认真,课堂效率高,我觉得一切都很好,暂时没有什么意见和建议",
"老师上课既幽默又严格,教学方法很适合我们,没有需要改进的地方",
"老师治学严谨,循循善诱,对老师的授课我非常满意,请老师保持这种教学状态",
"老师授课有条理有重点,我认为已经做得很到位了,没有什么建议可提",
"老师善于用凝练的语言讲解复杂内容,教学方式很好,希望老师继续发扬优点",
"老师讲课内容详细,条理清晰,我觉得没有什么需要调整的地方,一切都很棒",
"老师讲授认真,内容丰富,我对教学方式非常认可,请老师保持现在的风格",
"老师对待教学认真负责,能够调动学生积极性,我没有什么意见,希望老师继续保持",
"老师课堂效率高,气氛活跃,整节课学下来很有收获,暂时想不到需要改进的地方",
"老师教学态度端正,讲课思路清晰,我觉得非常好,没有什么意见和建议",
"老师授课生动有趣,深入浅出,对老师的教学我很满意,请老师保持下去",
"老师对学生要求严格但不失关怀,教学方法得当,我没有什么建议可提",
"老师讲课重点突出,内容充实,我认为一切都很好,希望老师继续保持",
"老师课堂互动性强,能照顾到每个学生,我觉得没有需要改进的地方",
"老师备课充分,讲解透彻,对老师的授课非常认可,暂时没有什么意见",
"老师教学经验丰富,方法多样,我觉得已经很优秀了,请老师保持现状",
"老师语言表达清晰,逻辑性强,我没有什么建议,希望老师继续发扬",
"老师授课节奏把握得很好,我认为非常合适,没有什么需要调整的",
"老师对待学生耐心负责,教学效果显著,我很满意,请老师保持",
"老师讲课富有激情,能感染学生,我觉得很好,暂时没有什么意见",
"老师专业知识扎实,讲解到位,对老师的教学我非常认可,没有建议",
"老师善于引导学生思考,启发性强,我认为一切都很好,请老师保持",
"老师课堂管理有序,教学效率高,我觉得没有什么需要改进的地方",
"老师授课风格独特,深受学生喜爱,我没有什么意见和建议",
"老师讲课深入浅出,通俗易懂,我认为非常好,希望老师继续保持",
"老师对学生一视同仁,公平公正,我很满意老师的教学方式",
"老师教学方法科学合理,效果突出,我觉得没有需要调整的地方",
"老师认真批改作业,及时反馈,对老师的工作我非常认可,请保持",
"老师课堂内容丰富多彩,讲解细致入微,我没有什么建议,一切都很好",
];
// 总体评价文案库
static const List<String> overallTexts = [
"老师讲课认真负责,课程内容充实丰富,理论与实践结合得很好,让我收获颇丰,对专业知识有了更深入的理解",
"老师授课条理清晰,课程设置合理,由浅入深,循序渐进,学习过程中既有挑战性又能跟上节奏",
"老师教学方法灵活多样,课程内容非常实用,学到的知识能够应用到实际中,让我感受到了学以致用的乐趣",
"老师讲课生动有趣,课程内容丰富多彩,涵盖面广,开阔了我的视野,激发了我对这个领域更浓厚的兴趣",
"老师治学严谨,循循善诱,通过这门课程让我建立了完整的知识体系,培养了逻辑思维能力和分析问题的能力",
"老师授课重点突出,课程难度适中,既巩固了基础知识,又拓展了深度内容,满足了我的学习需求",
"老师善于启发学生思考,课程注重培养实践能力和创新思维,让我不仅学到了知识,更学会了如何解决问题",
"老师讲解详细透彻,课程安排紧凑合理,通过学习让我对该学科有了系统而全面的认识",
"老师课堂气氛活跃,能调动学生积极性,这门课程很有启发性,培养了我的自主学习能力和探索精神",
"老师教学认真,内容讲授清晰明确,课程与时俱进,紧跟学科发展,整体学习体验非常好,让我受益匪浅",
"老师备课充分,课程内容环环相扣,逻辑严密,让我掌握了扎实的专业基础知识",
"老师授课富有激情,课程设计新颖独特,学习过程充满乐趣,让我对学习保持了浓厚兴趣",
"老师对学生认真负责,课程作业设置合理,既能巩固知识又不会过于繁重,学习效果很好",
"老师讲课深入浅出,课程内容由易到难,知识点讲解透彻,让我能够循序渐进地掌握知识",
"老师善于互动交流,课程注重学生参与,让我在积极的课堂氛围中提高了学习效率",
"老师专业素养高,课程内容前沿实用,让我了解到了学科的最新发展动态和应用前景",
"老师授课方式灵活,课程形式多样,既有理论讲解又有案例分析,让学习更加立体生动",
"老师对学生耐心指导,课程考核方式合理,既注重过程又关注结果,让我全面提升了能力",
"老师治学态度严谨,课程内容系统完整,帮助我构建了完善的知识框架和学科思维",
"老师善于举例说明,课程理论联系实际,让抽象的概念变得具体易懂,提高了我的理解能力",
"老师课堂管理有序,课程进度把握得当,既保证了教学质量又照顾到了学生的接受能力",
"老师讲课条理分明,课程重难点突出,让我能够抓住学习的关键,提高了学习效率",
"老师对学生要求严格,课程训练扎实有效,让我养成了良好的学习习惯和严谨的学习态度",
"老师授课语言生动,课程内容引人入胜,每节课都能让我保持高度的专注和学习热情",
"老师善于总结归纳,课程知识点梳理清晰,帮助我建立了清晰的知识脉络和记忆框架",
"老师注重能力培养,课程不仅传授知识更注重方法,让我掌握了学习和研究的基本方法",
"老师课堂效果显著,课程学习收获很大,不仅提升了专业水平也拓宽了思维视野",
"老师教学经验丰富,课程设计科学合理,让我在轻松愉快的氛围中完成了学习任务",
"老师对学生关怀备至,课程教学以学生为中心,充分考虑了我们的实际需求和接受能力",
"老师讲课精彩纷呈,课程内容充实饱满,每次上课都有新的收获和感悟,让我的学习充满期待",
];
/// Generate text based on question type
/// Returns a random text from the appropriate text library
String generate(QuestionType type) {
String text;
switch (type) {
case QuestionType.inspiration:
text = inspirationTexts[_random.nextInt(inspirationTexts.length)];
break;
case QuestionType.suggestion:
text = suggestionTexts[_random.nextInt(suggestionTexts.length)];
break;
case QuestionType.overall:
text = overallTexts[_random.nextInt(overallTexts.length)];
break;
case QuestionType.general:
// For general type, use overall texts as fallback
text = overallTexts[_random.nextInt(overallTexts.length)];
break;
}
// Apply text processing rules
text = _removeSpaces(text);
text = _ensureMinLength(text);
return text;
}
/// Ensure text has at least 4 characters
/// If text is shorter, pad with appropriate content
String _ensureMinLength(String text) {
if (text.length >= 4) {
return text;
}
// For very short texts like "无" or "没有", they are already valid
// Just return as is since Chinese characters count as valid
return text;
}
/// Remove all spaces from text
String _removeSpaces(String text) {
return text.replaceAll(' ', '');
}
/// Validate if text meets all requirements
/// Returns true if text is valid, false otherwise
bool validate(String text) {
// Check minimum length (at least 4 characters)
if (text.length < 4) {
return false;
}
// Check for spaces (should not contain any)
if (text.contains(' ')) {
return false;
}
// Check for 3 or more consecutive identical characters
if (_hasConsecutiveChars(text)) {
return false;
}
return true;
}
/// Check if text has 3 or more consecutive identical characters
/// Returns true if found, false otherwise
bool _hasConsecutiveChars(String text) {
if (text.length < 3) {
return false;
}
for (int i = 0; i < text.length - 2; i++) {
if (text[i] == text[i + 1] && text[i] == text[i + 2]) {
return true;
}
}
return false;
}
}