From 3a5f1ed679135d4c95a967a51e3d5cce1275bf77 Mon Sep 17 00:00:00 2001 From: AndyChen1024 Date: Thu, 3 Sep 2026 19:03:35 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20core=20=E5=A4=9A=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=AA=A8=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/common/build.gradle.kts | 3 ++ core/data/build.gradle.kts | 16 ++++++ core/data/src/main/AndroidManifest.xml | 2 + core/database/build.gradle.kts | 12 +++++ core/database/src/main/AndroidManifest.xml | 2 + core/datastore/build.gradle.kts | 12 +++++ core/datastore/src/main/AndroidManifest.xml | 2 + core/designsystem/build.gradle.kts | 7 +++ .../designsystem/src/main/AndroidManifest.xml | 2 + .../core/designsystem/theme/Color.kt | 11 ++++ .../core/designsystem/theme/Theme.kt | 50 +++++++++++++++++++ .../core/designsystem/theme/Type.kt | 6 +++ core/domain/DESIGN_NOTES.md | 22 ++++++++ core/domain/build.gradle.kts | 9 ++++ .../android_client/core/domain/UseCase.kt | 11 ++++ .../android_client/core/domain/UseCaseTest.kt | 17 +++++++ core/model/build.gradle.kts | 3 ++ core/network/build.gradle.kts | 18 +++++++ core/network/src/main/AndroidManifest.xml | 2 + core/ui/build.gradle.kts | 13 +++++ core/ui/src/main/AndroidManifest.xml | 2 + 21 files changed, 222 insertions(+) create mode 100644 core/common/build.gradle.kts create mode 100644 core/data/build.gradle.kts create mode 100644 core/data/src/main/AndroidManifest.xml create mode 100644 core/database/build.gradle.kts create mode 100644 core/database/src/main/AndroidManifest.xml create mode 100644 core/datastore/build.gradle.kts create mode 100644 core/datastore/src/main/AndroidManifest.xml create mode 100644 core/designsystem/build.gradle.kts create mode 100644 core/designsystem/src/main/AndroidManifest.xml create mode 100644 core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Color.kt create mode 100644 core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Theme.kt create mode 100644 core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Type.kt create mode 100644 core/domain/DESIGN_NOTES.md create mode 100644 core/domain/build.gradle.kts create mode 100644 core/domain/src/main/kotlin/com/mingyuan/android_client/core/domain/UseCase.kt create mode 100644 core/domain/src/test/kotlin/com/mingyuan/android_client/core/domain/UseCaseTest.kt create mode 100644 core/model/build.gradle.kts create mode 100644 core/network/build.gradle.kts create mode 100644 core/network/src/main/AndroidManifest.xml create mode 100644 core/ui/build.gradle.kts create mode 100644 core/ui/src/main/AndroidManifest.xml diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts new file mode 100644 index 0000000..9523466 --- /dev/null +++ b/core/common/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + alias(libs.plugins.mcl.kotlin.library) +} diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts new file mode 100644 index 0000000..341bdcb --- /dev/null +++ b/core/data/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + alias(libs.plugins.mcl.android.library) + alias(libs.plugins.mcl.android.hilt) +} + +android { + namespace = "com.mingyuan.android_client.core.data" +} + +dependencies { + implementation(project(":core:domain")) + implementation(project(":core:model")) + implementation(project(":core:network")) + implementation(project(":core:database")) + implementation(project(":core:datastore")) +} diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/core/data/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts new file mode 100644 index 0000000..eda0ca7 --- /dev/null +++ b/core/database/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + alias(libs.plugins.mcl.android.library) + alias(libs.plugins.mcl.android.room) +} + +android { + namespace = "com.mingyuan.android_client.core.database" +} + +dependencies { + implementation(project(":core:model")) +} diff --git a/core/database/src/main/AndroidManifest.xml b/core/database/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/core/database/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts new file mode 100644 index 0000000..a004782 --- /dev/null +++ b/core/datastore/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + alias(libs.plugins.mcl.android.library) +} + +android { + namespace = "com.mingyuan.android_client.core.datastore" +} + +dependencies { + implementation(project(":core:model")) + implementation(libs.androidx.datastore.preferences) +} diff --git a/core/datastore/src/main/AndroidManifest.xml b/core/datastore/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/core/datastore/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts new file mode 100644 index 0000000..ad7a985 --- /dev/null +++ b/core/designsystem/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + alias(libs.plugins.mcl.android.library.compose) +} + +android { + namespace = "com.mingyuan.android_client.core.designsystem" +} diff --git a/core/designsystem/src/main/AndroidManifest.xml b/core/designsystem/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/core/designsystem/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Color.kt b/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Color.kt new file mode 100644 index 0000000..97fe81e --- /dev/null +++ b/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Color.kt @@ -0,0 +1,11 @@ +package com.mingyuan.android_client.core.designsystem.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650A4) +val PurpleGrey40 = Color(0xFF625B71) +val Pink40 = Color(0xFF7D5260) diff --git a/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Theme.kt b/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Theme.kt new file mode 100644 index 0000000..8851eb8 --- /dev/null +++ b/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Theme.kt @@ -0,0 +1,50 @@ +package com.mingyuan.android_client.core.designsystem.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80, +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40, +) + +/** + * The app's single Material theme. All UI entry points wrap their content in [MingyuanTheme]. + */ +@Composable +fun MingyuanTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+. + dynamicColor: Boolean = true, + content: @Composable () -> Unit, +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content, + ) +} diff --git a/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Type.kt b/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Type.kt new file mode 100644 index 0000000..6715c0f --- /dev/null +++ b/core/designsystem/src/main/kotlin/com/mingyuan/android_client/core/designsystem/theme/Type.kt @@ -0,0 +1,6 @@ +package com.mingyuan.android_client.core.designsystem.theme + +import androidx.compose.material3.Typography + +// Uses Material 3 defaults; customize typography here as the design system evolves. +val Typography = Typography() diff --git a/core/domain/DESIGN_NOTES.md b/core/domain/DESIGN_NOTES.md new file mode 100644 index 0000000..7337ef1 --- /dev/null +++ b/core/domain/DESIGN_NOTES.md @@ -0,0 +1,22 @@ +# DESIGN_NOTES — core:domain + +## 为什么纯 Kotlin + +`core:domain`(以及 `core:model`、`core:common`)用 `mcl.kotlin.library`(`org.jetbrains.kotlin.jvm`),**不依赖 Android**。这样: + +- 业务规则(UseCase)不 import `android.*`,框架解耦,天然可移植。 +- 单元测试直接跑在 JVM 上(`./gradlew test`),快且稳定,无需 Robolectric/设备。 + +## 如何塑造 UseCase / Repository + +- **UseCase**:单一动作的接口,`suspend operator fun invoke(input): Output`(见 `UseCase.kt`)。一个类一个动作,符合单一职责。 +- **Repository 接口**:定义在 `core:domain`(接口),实现在 `core:data`(`XxxRepositoryImpl`)。调用方只依赖接口,不依赖实现,便于用 fake 替换做测试。 + +## 依赖方向 + +`core:domain` 只依赖 `core:model` 与 `core:common`(都是纯 Kotlin)。**不要**让 domain 依赖 Android、网络、数据库——那是数据层的职责。 + +## 约定 + +- 需要协程时用 `kotlinx-coroutines-core`(`Flow` 等);不要在 domain 里用 Android 的 `LiveData`。 +- 状态/结果优先用 sealed class 表达。 diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts new file mode 100644 index 0000000..4b59c5d --- /dev/null +++ b/core/domain/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + alias(libs.plugins.mcl.kotlin.library) +} + +dependencies { + implementation(libs.kotlinx.coroutines.core) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.truth) +} diff --git a/core/domain/src/main/kotlin/com/mingyuan/android_client/core/domain/UseCase.kt b/core/domain/src/main/kotlin/com/mingyuan/android_client/core/domain/UseCase.kt new file mode 100644 index 0000000..27b55f2 --- /dev/null +++ b/core/domain/src/main/kotlin/com/mingyuan/android_client/core/domain/UseCase.kt @@ -0,0 +1,11 @@ +package com.mingyuan.android_client.core.domain + +/** + * Base contract for a single-action use case. + * + * Keeping use cases as plain interfaces with a `suspend operator invoke` keeps domain logic + * framework-agnostic and trivially testable on the JVM (see [com.mingyuan.android_client.core.domain.UseCaseTest]). + */ +interface UseCase { + suspend operator fun invoke(input: Input): Output +} diff --git a/core/domain/src/test/kotlin/com/mingyuan/android_client/core/domain/UseCaseTest.kt b/core/domain/src/test/kotlin/com/mingyuan/android_client/core/domain/UseCaseTest.kt new file mode 100644 index 0000000..888451b --- /dev/null +++ b/core/domain/src/test/kotlin/com/mingyuan/android_client/core/domain/UseCaseTest.kt @@ -0,0 +1,17 @@ +package com.mingyuan.android_client.core.domain + +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class UseCaseTest { + + @Test + fun `invoke delegates to the implementation`() = runTest { + val double = object : UseCase { + override suspend fun invoke(input: Int): Int = input * 2 + } + + assertThat(double(21)).isEqualTo(42) + } +} diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts new file mode 100644 index 0000000..9523466 --- /dev/null +++ b/core/model/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + alias(libs.plugins.mcl.kotlin.library) +} diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts new file mode 100644 index 0000000..e38ceda --- /dev/null +++ b/core/network/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + alias(libs.plugins.mcl.android.library) + alias(libs.plugins.kotlin.serialization) +} + +android { + namespace = "com.mingyuan.android_client.core.network" +} + +dependencies { + implementation(project(":core:common")) + implementation(project(":core:model")) + + implementation(libs.kotlinx.serialization.json) + implementation(libs.retrofit.core) + implementation(libs.retrofit.kotlinx.serialization) + implementation(libs.okhttp.logging) +} diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/core/network/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts new file mode 100644 index 0000000..16a2769 --- /dev/null +++ b/core/ui/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + alias(libs.plugins.mcl.android.library.compose) + alias(libs.plugins.mcl.android.hilt) +} + +android { + namespace = "com.mingyuan.android_client.core.ui" +} + +dependencies { + implementation(project(":core:designsystem")) + implementation(project(":core:model")) +} diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/core/ui/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + +