chore: core 多模块骨架
This commit is contained in:
3
core/common/build.gradle.kts
Normal file
3
core/common/build.gradle.kts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.mcl.kotlin.library)
|
||||||
|
}
|
||||||
16
core/data/build.gradle.kts
Normal file
16
core/data/build.gradle.kts
Normal file
@@ -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"))
|
||||||
|
}
|
||||||
2
core/data/src/main/AndroidManifest.xml
Normal file
2
core/data/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
12
core/database/build.gradle.kts
Normal file
12
core/database/build.gradle.kts
Normal file
@@ -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"))
|
||||||
|
}
|
||||||
2
core/database/src/main/AndroidManifest.xml
Normal file
2
core/database/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
12
core/datastore/build.gradle.kts
Normal file
12
core/datastore/build.gradle.kts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
2
core/datastore/src/main/AndroidManifest.xml
Normal file
2
core/datastore/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
7
core/designsystem/build.gradle.kts
Normal file
7
core/designsystem/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.mcl.android.library.compose)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.mingyuan.android_client.core.designsystem"
|
||||||
|
}
|
||||||
2
core/designsystem/src/main/AndroidManifest.xml
Normal file
2
core/designsystem/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
@@ -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)
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
22
core/domain/DESIGN_NOTES.md
Normal file
22
core/domain/DESIGN_NOTES.md
Normal file
@@ -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 表达。
|
||||||
9
core/domain/build.gradle.kts
Normal file
9
core/domain/build.gradle.kts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -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<in Input, out Output> {
|
||||||
|
suspend operator fun invoke(input: Input): Output
|
||||||
|
}
|
||||||
@@ -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<Int, Int> {
|
||||||
|
override suspend fun invoke(input: Int): Int = input * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(double(21)).isEqualTo(42)
|
||||||
|
}
|
||||||
|
}
|
||||||
3
core/model/build.gradle.kts
Normal file
3
core/model/build.gradle.kts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.mcl.kotlin.library)
|
||||||
|
}
|
||||||
18
core/network/build.gradle.kts
Normal file
18
core/network/build.gradle.kts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
2
core/network/src/main/AndroidManifest.xml
Normal file
2
core/network/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
13
core/ui/build.gradle.kts
Normal file
13
core/ui/build.gradle.kts
Normal file
@@ -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"))
|
||||||
|
}
|
||||||
2
core/ui/src/main/AndroidManifest.xml
Normal file
2
core/ui/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest />
|
||||||
Reference in New Issue
Block a user