# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Android 项目特定说明 ### 项目概况 **项目名称**: AirLogistics - 航空物流信息管理系统 **项目类型**: Android 原生应用 **架构模式**: MVVM + 组件化 **开发语言**: Kotlin 1.6.21 + Java **当前版本**: 1.8.4 (versionCode 84) **SDK 版本要求**: - minSdkVersion: 24 (Android 7.0) - targetSdkVersion: 30 (Android 10) - compileSdkVersion: 31 ### 核心架构 #### MVVM 基类体系 - **BaseActivity**: 提供协程支持、Loading管理、扫码功能、键盘控制 - **BaseBindingActivity**: DataBinding自动绑定、ViewModel生命周期管理 - **BaseViewModel**: Loading管理、Activity结果处理 - **BasePageViewModel**: 分页列表专用,集成PageModel自动处理分页 - **CommonAdapter + BaseViewHolder**: 统一列表适配器封装 #### 组件化模块划分 - **app/**: 应用壳层,整合所有业务模块 - **module_base/**: 核心基础库(MVVM基类、网络框架、UI组件) - **module_gnc/**: 国内出港业务模块 - **module_gnj/**: 国内进港业务模块 - **module_gjc/**: 国际出港业务模块 - **module_gjj/**: 国际进港业务模块 - **module_hangban/**: 航班管理模块 - **module_cargo/**: 货物追踪模块 - **module_mit/**: 监装监卸管理模块 - **module_p/**: PDA专用功能模块 - **Printer/**: 蓝牙打印模块 - **MPChartLib/**: 图表库模块 #### 模块间通信 - **路由**: ARouter 1.5.2 实现模块间页面跳转 - **事件总线**: FlowBus(基于Flow) + EventBus 3.1.1 - **依赖注入**: 基于ServiceLoader的服务发现机制 #### 网络请求框架 - **技术栈**: Retrofit 2.6.1 + OkHttp 3.12.12 + Kotlin Coroutines - **扩展函数**: - `launchCollect`: 无Loading的后台请求 - `launchLoadingCollect`: 带Loading的关键操作 - `toRequestBody`: Map/Bean自动转JSON - **拦截器**: 自动添加Token、时间戳,统一错误处理 ### 关键目录结构 ``` aerologic-app/ ├── app/src/main/java/com/lukouguoji/aerologic/ │ ├── ui/viewModel/ # ViewModel文件 │ ├── ui/fragment/ # Fragment文件 (HomeFragment, MineFragment等) │ └── page/ # 业务页面 ├── module_base/src/main/java/com/lukouguoji/module_base/ │ ├── BaseActivity.kt # 基础Activity类 │ ├── BaseFragment.kt # 基础Fragment类 │ ├── bean/ # 数据模型 (BaseResultBean, BaseListBean) │ ├── service/viewModel/ # ViewModel层 │ ├── ui/page/ # UI页面 │ ├── ui/weight/ # 自定义UI组件 (PadSearchLayout, PadDataLayout) │ ├── http/ # 网络请求框架 │ └── ktx/ # Kotlin扩展函数 ├── module_gnc/src/main/ # 国内出港业务代码 ├── module_gnj/src/main/ # 国内进港业务代码 └── 其他业务模块... ``` ### 开发规范 #### 命名约定 - **Activity**: `XxxActivity` (例: `LoginActivity`) - **Fragment**: `XxxFragment` (例: `HomeFragment`) - **ViewModel**: `XxxViewModel` (例: `LoginViewModel`) - **Adapter**: `XxxAdapter` (例: `CargoListAdapter`) - **ViewHolder**: `XxxViewHolder` (例: `CargoItemViewHolder`) - **Layout文件**: `activity_xxx.xml`, `fragment_xxx.xml`, `item_xxx.xml` #### 文件组织规范 - 业务页面放在对应模块的 `ui/page/` 目录下 - ViewModel放在 `service/viewModel/` 目录下 - 数据模型放在 `bean/` 目录下 - 适配器放在 `adapter/` 目录下 #### DataBinding 使用要点 - 布局文件使用 `` 标签包裹 - 定义 `` 绑定 ViewModel - 使用 `@{}` 表达式进行数据绑定 - Activity/Fragment 中使用 `DataBindingUtil` 或自动生成的 Binding 类 #### 协程使用规范 - 在 ViewModel 中使用 `viewModelScope` 启动协程 - 网络请求使用 `launchCollect` 或 `launchLoadingCollect` 扩展函数 - Flow 用于响应式数据流处理 - 使用 `withContext(Dispatchers.IO)` 进行IO操作 ### 常用构建命令 ```bash # 清理构建缓存 ./gradlew clean # 构建 Debug APK ./gradlew assembleDebug # 构建 Release APK (已签名) ./gradlew assembleRelease # 安装到设备 ./gradlew installDebug # 运行 Lint 检查 ./gradlew lint # 查看已连接设备 adb devices -l # 查看应用日志 adb logcat | grep "com.lukouguoji.aerologic" ``` ### 快捷命令 项目已配置以下快捷命令 (在 `.claude/commands/` 目录): - `/build-debug` - 构建 Debug APK - `/build-release` - 构建 Release APK - `/install` - 安装到设备 - `/clean-build` - 清理并构建 - `/check-modules` - 检查所有模块 - `/lint` - 运行代码检查 - `/devices` - 列出已连接设备 - `/logs` - 查看应用日志 ### 组件化开发模式 项目支持模块独立运行调试: 1. 编辑 `gradle.properties` 2. 设置 `isBuildModule=true` (独立模式) 或 `false` (集成模式) 3. Sync项目并运行对应模块 **注意**: 独立模式下,各模块作为独立应用运行;集成模式下,所有模块整合到app壳层。 ### 环境配置 #### 开发环境要求 - **IDE**: Android Studio Arctic Fox (2020.3.1) 或更高版本 - **JDK**: 1.8 - **Gradle**: 7.3.3 - **Kotlin**: 1.6.21 #### 服务器配置 - **配置文件**: `module_base/src/main/res/values/strings.xml` - **主服务器**: `system_url_inner` - **地磅服务器**: `weight_url` - **运行时**: 可通过 SharedPreferences 动态修改IP地址 #### 签名配置 - **KeyStore**: `key.jks` (项目根目录) - **Store密码**: `123321` - **Key密码**: `123321` - **别名**: `key` ### 常见问题解决 #### 依赖下载失败 1. 检查网络连接 2. 使用阿里云Maven镜像 (已在 build.gradle 中配置) 3. 如需手动配置 Gradle,参考 README.md 中的依赖配置章节 #### 模块编译错误 1. 执行 `./gradlew clean` 2. 检查 `gradle.properties` 中的 `isBuildModule` 设置 3. Sync Project with Gradle Files #### ADB 连接问题 ```bash # 重启 ADB 服务 adb kill-server && adb start-server # 查看设备连接状态 adb devices -l # 无线调试 (Android 11+) adb pair : adb connect : ``` --- ## 详细开发指南 ### 标准代码模板 #### Activity 模板 ```kotlin @Route(path = ARouterConstants.ACTIVITY_URL_XXX) class XxxActivity : BaseBindingActivity() { override fun layoutId() = R.layout.activity_xxx override fun viewModelClass() = XxxViewModel::class.java override fun initOnCreate(savedInstanceState: Bundle?) { setBackArrow("页面标题") binding.viewModel = viewModel // 初始化UI } } ``` #### ViewModel 模板 **列表页 ViewModel:** ```kotlin class XxxListViewModel : BasePageViewModel() { val searchText = MutableLiveData() val itemLayoutId = R.layout.item_xxx val itemViewHolder = XxxViewHolder::class.java override fun getData() { val params = mapOf( "page" to pageModel.page, "limit" to pageModel.limit, "searchText" to searchText.value ).toRequestBody() launchLoadingCollect({ NetApply.api.getXxxList(params) }) { onSuccess = { pageModel.handleListBean(it) } } } override fun onItemClick(position: Int, type: Int) { val bean = pageModel.rv!!.commonAdapter()!!.getItem(position) as XxxBean // 跳转详情 } } ``` **详情页 ViewModel:** ```kotlin class XxxDetailsViewModel : BaseViewModel() { var id = "" val dataBean = MutableLiveData() fun initOnCreated(intent: Intent) { id = intent.getStringExtra(Constant.Key.ID) ?: "" getData() } private fun getData() { launchLoadingCollect({ NetApply.api.getXxxDetails(id) }) { onSuccess = { dataBean.value = it.data ?: XxxBean() } } } } ``` **编辑页 ViewModel:** ```kotlin class XxxAddViewModel : BaseViewModel() { val pageType = MutableLiveData(DetailsPageType.Add) // 必须用LiveData var id = "" val dataBean = MutableLiveData(XxxBean()) fun initOnCreated(intent: Intent) { pageType.value = DetailsPageType.valueOf( intent.getStringExtra(Constant.Key.PAGE_TYPE) ?: DetailsPageType.Add.name ) if (pageType.value != DetailsPageType.Add) { id = intent.getStringExtra(Constant.Key.ID) ?: "" loadData() } } fun submit() { val bean = dataBean.value ?: return if (bean.name.verifyNullOrEmpty("请输入名称")) return launchLoadingCollect({ val params = mapOf("id" to id, "name" to bean.name) .toRequestBody(removeEmptyOrNull = true) NetApply.api.saveXxx(params) }) { onSuccess = { showToast("保存成功") viewModelScope.launch { FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") } getTopActivity().finish() } } } } ``` ### DataBinding + LiveData 核心知识 #### 最关键的设置 (最常见错误) **必须在 Activity 中设置 lifecycleOwner,否则 XML 中的 LiveData 不会自动更新 UI!** ```kotlin override fun initOnCreate(savedInstanceState: Bundle?) { setBackArrow("页面标题") binding.viewModel = viewModel // ⚠️ 关键:必须设置,否则 LiveData 无法自动更新 UI binding.lifecycleOwner = this } ``` **BaseBindingActivity 已自动设置**,但如果手动使用 DataBinding 时务必记住! #### XML 中 LiveData 的绑定方式 **1. 单向绑定 `@{}`(只显示,ViewModel → UI)** ```xml ``` **2. 双向绑定 `@={}`(可编辑,UI ↔ ViewModel)** ```xml ``` **双向绑定要求**: - 字段必须是 `MutableLiveData` - 用户输入时自动更新 ViewModel 的值 - ViewModel 更新值时自动更新 UI **3. 点击事件绑定** ```xml