# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 项目概述 **AirLogistics (航空物流信息App)** - Android原生应用,用于管理航空物流的全流程操作,包括国内外货物进出港、仓储管理、车辆调度等核心业务。 - **包名**: com.lukouguoji.aerologic - **当前版本**: 1.7.9 (versionCode 79) - **开发语言**: Kotlin + Java混合 - **架构模式**: MVVM + 组件化 - **最低SDK**: Android 7.0 (API 24) - **目标SDK**: Android 10 (API 30) ## 构建与运行 ### 环境准备 1. **依赖下载问题解决**: - 下载gradle-7.3.3-bin.zip: https://pan.baidu.com/s/18wsuGRlNxjMYbxLhBH9yeg (提取码: 1029) - 打开 Settings -> Build, Execution, Deployment > Build Tools > Gradle - 将下载的文件解压后替换到 "Gradle user home" 目录中 2. **配置IP地址**: - 内网地址配置在 `module_base/src/main/res/values/strings.xml` 中的 `system_url_inner` - 地磅地址: `weight_url` - 运行时可通过SharedPreferences修改IP地址 ### 构建命令 ```bash # 组件化开发模式切换 # 编辑 gradle.properties 中的 isBuildModule # true: 模块可独立运行调试 # false: 模块作为library集成(默认) # 构建Debug版本 ./gradlew assembleDebug # 构建Release版本(已签名) ./gradlew assembleRelease # 安装到设备 ./gradlew installDebug # 清理构建 ./gradlew clean ``` ### 测试命令 ```bash # 运行单元测试 ./gradlew test # 运行特定模块的测试 ./gradlew :module_base:test ./gradlew :app:test # 运行UI测试 ./gradlew connectedAndroidTest ``` ## 核心架构 ### 模块化结构 项目采用**组件化架构**,通过`isBuildModule`参数控制模块独立运行或作为library集成: - **app**: 应用壳层,整合所有业务模块,提供主界面框架 - **module_base**: 核心基础库(可独立运行),提供所有通用能力 - **module_gnc**: 国内出港业务(收运、复磅、装机等) - **module_gnj**: 国内进港业务(卸机、提货、移库等) - **module_gjc**: 国际出港业务(板箱组装、ULD管理等) - **module_gjj**: 国际进港业务(舱单、理货、交接等) - **module_hangban**: 航班查询管理 - **module_cargo**: 货物追踪查询 - **module_mit**: 监装监卸管理 - **module_p**: PDA专用功能 - **Printer**: 蓝牙打印模块(佳博SDK) - **MPChartLib**: 定制图表库 ### MVVM架构模式 所有业务页面遵循统一的MVVM模式: ``` Activity/Fragment (View层) ↓ 继承 BaseBindingActivity ↓ 持有 ViewModel (业务逻辑层) ↓ 继承 BaseViewModel / BasePageViewModel ↓ 调用 Repository (数据层: Retrofit API) ``` **关键基类**: - `BaseBindingActivity`: 提供DataBinding和ViewModel绑定 - `BaseViewModel`: 提供Loading管理、Lifecycle感知 - `BasePageViewModel`: 扩展分页列表功能 - `CommonAdapter + BaseViewHolder`: 列表适配器统一封装 ### 网络请求架构 **核心组件**: - `ServiceCreator`: Retrofit实例创建,管理BaseURL和拦截器 - `NetApply.api`: 统一的API调用入口 - `RequestKtx.kt`: 协程+Flow的扩展函数封装 **标准请求模式**: ```kotlin // 使用launchCollect扩展函数(无Loading) launchCollect({ NetApply.api.simplePost("endpoint", params.toRequestBody()) }) { onSuccess = { result: BaseResultBean -> // 成功处理,result.data为返回数据 } onFailed = { code, message -> // 失败处理,默认会showToast(message) } onComplete = { // 请求完成(无论成功失败) } } // 使用launchLoadingCollect(带Loading弹窗) launchLoadingCollect({ NetApply.api.simplePost("endpoint", params.toRequestBody()) }) { onSuccess = { result -> } onFailed = { code, message -> } } ``` **API接口定义** (在 `module_base/http/user/interface/CommonService.kt`): ```kotlin @POST("{url}") suspend fun simplePost( @Path("url", encoded = true) url: String, @Body data: RequestBody ): BaseResultBean ``` ### 路由系统 使用**ARouter**进行模块间页面跳转: ```kotlin // 路由常量定义在 ARouterConstants ARouter.getInstance() .build(ARouterConstants.ACTIVITY_URL_XXX) .withString("key", value) .navigation() // 获取参数 @Autowired(name = "key") @JvmField var param: String? = null // 在onCreate中调用 ARouter.getInstance().inject(this) ``` ### 事件通信 **FlowBus** (module_base/impl/FlowBus.kt) - 基于Kotlin Flow的事件总线: ```kotlin // 发送事件 FlowBus.post(ConstantEvent.EVENT_NAME, data) // 接收事件(在ViewModel中) FlowBus.on(ConstantEvent.EVENT_NAME).collect { data -> // 处理事件 } ``` 常用事件常量定义在 `ConstantEvent.kt`。 ### 数据模型规范 所有API返回数据统一使用 `BaseResultBean`: ```kotlin data class BaseResultBean( val status: Int, // 状态码 val msg: String, // 消息 val data: T // 实际数据 ) ``` 业务Bean统一定义在 `module_base/bean/` 目录下,使用Kotlin data class。 ## 关键技术点 ### 屏幕适配 - **横屏设计尺寸**: 1152dp × 720dp(主要场景) - **竖屏设计尺寸**: 720dp × 1280dp - **强制横屏**: 所有Activity在AndroidManifest中配置 `android:screenOrientation="userLandscape"` - **适配库**: AutoSize 1.2.+ ### 蓝牙打印 **核心类**: - `Printer` 模块:独立的打印服务 - `PrinterService`: 后台打印服务 - `PrinterConfig`: 打印机配置管理 - 使用佳博SDK 2.0.4 **打印流程**: 1. 启动PrinterService 2. 扫描/绑定蓝牙打印机 3. 构建打印数据(DataForSendToPrinter) 4. 发送打印任务 ### 扫码功能 - 使用 ZXing 2.2.9 库 - 扫描条形码/二维码 - 主要用于:运单号、ULD编号、车辆编号、板箱编号 ### 权限管理 使用 AndPermission 2.0.2: ```kotlin AndPermission.with(this) .runtime() .permission(Permission.CAMERA) .onGranted { } .onDenied { } .start() ``` ### 图片选择 使用 PictureSelector v3.11.2 + Glide 4.15.1: ```kotlin PictureSelector.create(this) .openGallery(SelectMimeType.ofImage()) .setImageEngine(GlideEngine.createGlideEngine()) .forResult { result -> } ``` ## 开发规范 ### 新增功能页面 1. **创建页面结构** (按 list/details/add 组织): ``` page/ └── feature/ ├── list/ │ ├── FeatureListActivity.kt │ ├── FeatureListViewModel.kt │ └── FeatureListViewHolder.kt ├── details/ │ ├── FeatureDetailsActivity.kt │ └── FeatureDetailsViewModel.kt └── add/ ├── FeatureAddActivity.kt └── FeatureAddViewModel.kt ``` 2. **在ARouterConstants中注册路由**: ```kotlin const val ACTIVITY_URL_FEATURE_LIST = "/feature/list" ``` 3. **在CommonService中添加API接口**: ```kotlin @POST("api/feature/list") suspend fun getFeatureList(@Body data: RequestBody): BaseResultBean> ``` 4. **创建数据Bean** (在module_base/bean/): ```kotlin data class FeatureBean( val id: String, val name: String // 其他字段... ) ``` ### Activity开发模板 ```kotlin @Route(path = ARouterConstants.ACTIVITY_URL_XXX) class XxxActivity : BaseBindingActivity() { override fun getViewBinding() = ActivityXxxBinding.inflate(layoutInflater) override fun getViewModel() = ViewModelProvider(this)[XxxViewModel::class.java] override fun initView() { // 初始化UI binding.apply { viewModel = this@XxxActivity.viewModel } } override fun initData() { // 加载数据 viewModel.loadData() } override fun addListener() { // 添加监听器 } override fun addObserver() { // 观察LiveData/Flow } } ``` ### ViewModel开发模板 ```kotlin class XxxViewModel : BasePageViewModel() { // LiveData定义 val dataList = MutableLiveData>() fun loadData() { launchLoadingCollect({ NetApply.api.simplePost("endpoint", params.toRequestBody()) }) { onSuccess = { result -> dataList.value = result.data } } } } ``` ### 列表开发模板 ```kotlin // ViewHolder class XxxViewHolder(itemView: View) : BaseViewHolder(itemView) { override fun onBindViewHolder(item: XxxBean, position: Int) { // 绑定数据到视图 } } // Activity中使用 val adapter = CommonAdapter( R.layout.item_xxx, dataList ) { XxxViewHolder(it) } binding.recyclerView.adapter = adapter ``` ## 常见业务场景 ### 扫码后查询 ```kotlin // 启动扫码 ARouter.getInstance() .build(ARouterConstants.ACTIVITY_URL_SCAN) .navigation(this, REQUEST_CODE_SCAN) // 处理扫码结果 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_CODE_SCAN && resultCode == RESULT_OK) { val code = data?.getStringExtra("code") // 调用查询接口 } } ``` ### 打印标签 ```kotlin // 绑定打印服务 bindService(Intent(this, PrinterService::class.java), serviceConnection, BIND_AUTO_CREATE) // 打印 val printData = DataForSendToPrinter() // 构建打印内容... printerService?.sendPrintData(printData) ``` ### 图片上传 ```kotlin // 选择图片 PictureSelector.create(this) .openGallery(SelectMimeType.ofImage()) .forResult { result -> val path = result[0].realPath uploadImage(path) } // 上传接口 @Multipart @POST("api/upload") suspend fun uploadImage(@Part file: MultipartBody.Part): BaseResultBean ``` ## 重要配置文件 ### 签名配置 - **KeyStore**: `key.jks` (项目根目录) - **密码**: storePassword/keyPassword均为 `123321` - **别名**: `key` ### 网络配置 - **超时时间**: 30秒(连接/读取/写入) - **认证方式**: Bearer Token(通过拦截器自动添加) - **Token存储**: SharedPreferences (key: Constant.Share.token) - **网络安全配置**: `res/xml/network_security_config.xml` (支持HTTP) ### 数据持久化 - **SharedPreferences**: IP地址、Token、用户信息、角色 - **关键常量**: 定义在 `Constant.kt` 和 `ConstantEvent.kt` ## Git分支管理 - **当前开发分支**: feature/hefei - **主分支**: develop(用于PR) - **提交前**: 确保代码通过编译,无明显错误 ## 技术栈速查 - **协程**: kotlinx-coroutines 1.6.0 - **网络**: Retrofit 2.6.1 + OkHttp 3.12.12 - **JSON**: FastJSON 1.2.73 + Gson 2.10.1 - **路由**: ARouter 1.5.2 - **下拉刷新**: SmartRefreshLayout 2.0.3 - **图表**: MPAndroidChart (定制版) - **弹窗**: XPopup 2.9.19 - **图片**: Glide 4.15.1 + PictureSelector v3.11.2 - **扫码**: ZXing 2.2.9 - **权限**: AndPermission 2.0.2 - **打印**: 佳博SDK 2.0.4 - **日志**: Timber 5.0.1 - **事件**: EventBus 3.1.1 + FlowBus