diff --git a/android/app/src/main/java/com/tiangong/aiagent/data/remote/dto/Dtos.kt b/android/app/src/main/java/com/tiangong/aiagent/data/remote/dto/Dtos.kt index 70f099f..9cd3315 100644 --- a/android/app/src/main/java/com/tiangong/aiagent/data/remote/dto/Dtos.kt +++ b/android/app/src/main/java/com/tiangong/aiagent/data/remote/dto/Dtos.kt @@ -266,7 +266,9 @@ data class RegisterRequest( val username: String, val password: String, val email: String = "", - @SerializedName("display_name") val displayName: String? = null + @SerializedName("display_name") val displayName: String? = null, + @SerializedName("agreed_terms") val agreedTerms: Boolean = false, + @SerializedName("agreed_terms_version") val agreedTermsVersion: String? = null ) data class RegisterResponse( diff --git a/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt b/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt index bf8b93b..1efb9af 100644 --- a/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt +++ b/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginScreen.kt @@ -217,7 +217,42 @@ fun LoginScreen( ) } - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(16.dp)) + + // Agreement checkbox + val context = LocalContext.current + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + checked = uiState.agreedTerms, + onCheckedChange = viewModel::onAgreedTermsChanged, + enabled = !uiState.isLoading + ) + Text("我已阅读并同意", style = MaterialTheme.typography.bodySmall) + TextButton( + onClick = { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://tiangong.ai/privacy")) + context.startActivity(intent) + }, + contentPadding = PaddingValues(horizontal = 2.dp) + ) { + Text("《隐私政策》", style = MaterialTheme.typography.bodySmall) + } + Text("和", style = MaterialTheme.typography.bodySmall) + TextButton( + onClick = { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://tiangong.ai/terms")) + context.startActivity(intent) + }, + contentPadding = PaddingValues(horizontal = 2.dp) + ) { + Text("《用户协议》", style = MaterialTheme.typography.bodySmall) + } + } + + Spacer(modifier = Modifier.height(16.dp)) // Login button Button( @@ -228,7 +263,7 @@ fun LoginScreen( modifier = Modifier .fillMaxWidth() .height(50.dp), - enabled = !uiState.isLoading + enabled = !uiState.isLoading && uiState.agreedTerms ) { if (uiState.isLoading) { CircularProgressIndicator( @@ -251,7 +286,6 @@ fun LoginScreen( Spacer(modifier = Modifier.height(24.dp)) // Privacy policy & user agreement - val context = LocalContext.current PrivacyPolicyRow( onPrivacyPolicy = { val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://tiangong.ai/privacy")) diff --git a/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginViewModel.kt b/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginViewModel.kt index cf1f0c4..25519a8 100644 --- a/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginViewModel.kt +++ b/android/app/src/main/java/com/tiangong/aiagent/ui/login/LoginViewModel.kt @@ -16,6 +16,7 @@ data class LoginUiState( val username: String = "", val password: String = "", val serverUrl: String = "", + val agreedTerms: Boolean = false, val isLoading: Boolean = false, val error: String? = null, val isLoggedIn: Boolean = false @@ -50,6 +51,10 @@ class LoginViewModel @Inject constructor( _uiState.value = _uiState.value.copy(serverUrl = url, error = null) } + fun onAgreedTermsChanged(value: Boolean) { + _uiState.value = _uiState.value.copy(agreedTerms = value, error = null) + } + fun saveServerUrl(url: String) { val trimmed = url.trim().trimEnd('/') if (trimmed.isBlank()) { diff --git a/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterScreen.kt b/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterScreen.kt index df40886..071e9b8 100644 --- a/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterScreen.kt +++ b/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.* +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -178,13 +179,47 @@ fun RegisterScreen( Spacer(modifier = Modifier.height(8.dp)) + // Agreement checkbox + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + checked = uiState.agreedTerms, + onCheckedChange = viewModel::onAgreedTermsChanged, + enabled = !uiState.isLoading + ) + Text( + text = "我已阅读并同意", + style = MaterialTheme.typography.bodySmall + ) + TextButton( + onClick = { /* TODO: open privacy policy in-app */ }, + contentPadding = PaddingValues(horizontal = 2.dp) + ) { + Text("《隐私政策》", style = MaterialTheme.typography.bodySmall) + } + Text( + text = "和", + style = MaterialTheme.typography.bodySmall + ) + TextButton( + onClick = { /* TODO: open user agreement in-app */ }, + contentPadding = PaddingValues(horizontal = 2.dp) + ) { + Text("《用户协议》", style = MaterialTheme.typography.bodySmall) + } + } + + Spacer(modifier = Modifier.height(8.dp)) + // Register button Button( onClick = { focusManager.clearFocus() viewModel.register() }, - enabled = !uiState.isLoading, + enabled = !uiState.isLoading && uiState.agreedTerms, modifier = Modifier.fillMaxWidth().height(50.dp) ) { if (uiState.isLoading) { diff --git a/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterViewModel.kt b/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterViewModel.kt index 7d74d19..b9ccf7a 100644 --- a/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterViewModel.kt +++ b/android/app/src/main/java/com/tiangong/aiagent/ui/register/RegisterViewModel.kt @@ -18,6 +18,7 @@ data class RegisterUiState( val confirmPassword: String = "", val email: String = "", val displayName: String = "", + val agreedTerms: Boolean = false, val isUsernameValid: Boolean = true, val isPasswordValid: Boolean = true, @@ -96,6 +97,10 @@ class RegisterViewModel @Inject constructor( _uiState.update { it.copy(displayName = value) } } + fun onAgreedTermsChanged(value: Boolean) { + _uiState.update { it.copy(agreedTerms = value) } + } + fun clearError() { _uiState.update { it.copy(errorMessage = null) } } @@ -137,6 +142,10 @@ class RegisterViewModel @Inject constructor( } if (!usernameOk || !passwordOk || !confirmOk || !emailOk) return + if (!state.agreedTerms) { + _uiState.update { it.copy(errorMessage = "请先阅读并同意用户协议和隐私政策") } + return + } _uiState.update { it.copy(isLoading = true, errorMessage = null) } @@ -145,7 +154,9 @@ class RegisterViewModel @Inject constructor( username = state.username.trim(), password = state.password, email = state.email.trim(), - displayName = state.displayName.trim().ifBlank { null } + displayName = state.displayName.trim().ifBlank { null }, + agreedTerms = true, + agreedTermsVersion = "1.0" ) val result = authRepository.register(request) result.fold( diff --git a/backend/scripts/templates/template_customer_service.py b/backend/scripts/templates/template_customer_service.py index 8d29fb5..1304e7a 100644 --- a/backend/scripts/templates/template_customer_service.py +++ b/backend/scripts/templates/template_customer_service.py @@ -17,8 +17,8 @@ import time import requests BASE = os.getenv("PLATFORM_BASE_URL", "http://127.0.0.1:8037").rstrip("/") -USER = os.getenv("PLATFORM_USERNAME", "admin") -PWD = os.getenv("PLATFORM_PASSWORD", "123456") +USER = os.getenv("PLATFORM_USERNAME") +PWD = os.getenv("PLATFORM_PASSWORD") TEMPLATE_ID = "template_customer_service" DEFAULT_NAME = os.getenv("TARGET_NAME") or f"场景模板_客服_{int(time.time())}" diff --git a/backend/scripts/templates/template_dev_codegen.py b/backend/scripts/templates/template_dev_codegen.py index 0e89a50..4217153 100644 --- a/backend/scripts/templates/template_dev_codegen.py +++ b/backend/scripts/templates/template_dev_codegen.py @@ -14,8 +14,8 @@ import time import requests BASE = os.getenv("PLATFORM_BASE_URL", "http://127.0.0.1:8037").rstrip("/") -USER = os.getenv("PLATFORM_USERNAME", "admin") -PWD = os.getenv("PLATFORM_PASSWORD", "123456") +USER = os.getenv("PLATFORM_USERNAME") +PWD = os.getenv("PLATFORM_PASSWORD") TEMPLATE_ID = "template_dev_codegen" DEFAULT_NAME = os.getenv("TARGET_NAME") or f"场景模板_研发_{int(time.time())}" diff --git a/backend/scripts/templates/template_ops_log_analysis.py b/backend/scripts/templates/template_ops_log_analysis.py index 9b80152..8078393 100644 --- a/backend/scripts/templates/template_ops_log_analysis.py +++ b/backend/scripts/templates/template_ops_log_analysis.py @@ -14,8 +14,8 @@ import time import requests BASE = os.getenv("PLATFORM_BASE_URL", "http://127.0.0.1:8037").rstrip("/") -USER = os.getenv("PLATFORM_USERNAME", "admin") -PWD = os.getenv("PLATFORM_PASSWORD", "123456") +USER = os.getenv("PLATFORM_USERNAME") +PWD = os.getenv("PLATFORM_PASSWORD") TEMPLATE_ID = "template_ops_log_analysis" DEFAULT_NAME = os.getenv("TARGET_NAME") or f"场景模板_运维_{int(time.time())}"