11 KiB
11 KiB
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)
构建与运行
环境准备
-
依赖下载问题解决:
- 下载gradle-7.3.3-bin.zip: https://pan.baidu.com/s/18wsuGRlNxjMYbxLhBH9yeg (提取码: 1029)
- 打开 Settings -> Build, Execution, Deployment > Build Tools > Gradle
- 将下载的文件解压后替换到 "Gradle user home" 目录中
-
配置IP地址:
- 内网地址配置在
module_base/src/main/res/values/strings.xml中的system_url_inner - 地磅地址:
weight_url - 运行时可通过SharedPreferences修改IP地址
- 内网地址配置在
构建命令
# 组件化开发模式切换
# 编辑 gradle.properties 中的 isBuildModule
# true: 模块可独立运行调试
# false: 模块作为library集成(默认)
# 构建Debug版本
./gradlew assembleDebug
# 构建Release版本(已签名)
./gradlew assembleRelease
# 安装到设备
./gradlew installDebug
# 清理构建
./gradlew clean
测试命令
# 运行单元测试
./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<ViewDataBinding, ViewModel>
↓ 持有
ViewModel (业务逻辑层)
↓ 继承
BaseViewModel / BasePageViewModel
↓ 调用
Repository (数据层: Retrofit API)
关键基类:
BaseBindingActivity: 提供DataBinding和ViewModel绑定BaseViewModel: 提供Loading管理、Lifecycle感知BasePageViewModel: 扩展分页列表功能CommonAdapter + BaseViewHolder: 列表适配器统一封装
网络请求架构
核心组件:
ServiceCreator: Retrofit实例创建,管理BaseURL和拦截器NetApply.api: 统一的API调用入口RequestKtx.kt: 协程+Flow的扩展函数封装
标准请求模式:
// 使用launchCollect扩展函数(无Loading)
launchCollect({
NetApply.api.simplePost("endpoint", params.toRequestBody())
}) {
onSuccess = { result: BaseResultBean<T> ->
// 成功处理,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):
@POST("{url}")
suspend fun simplePost(
@Path("url", encoded = true) url: String,
@Body data: RequestBody
): BaseResultBean<T>
路由系统
使用ARouter进行模块间页面跳转:
// 路由常量定义在 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的事件总线:
// 发送事件
FlowBus.post(ConstantEvent.EVENT_NAME, data)
// 接收事件(在ViewModel中)
FlowBus.on<T>(ConstantEvent.EVENT_NAME).collect { data ->
// 处理事件
}
常用事件常量定义在 ConstantEvent.kt。
数据模型规范
所有API返回数据统一使用 BaseResultBean<T>:
data class BaseResultBean<T>(
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
打印流程:
- 启动PrinterService
- 扫描/绑定蓝牙打印机
- 构建打印数据(DataForSendToPrinter)
- 发送打印任务
扫码功能
- 使用 ZXing 2.2.9 库
- 扫描条形码/二维码
- 主要用于:运单号、ULD编号、车辆编号、板箱编号
权限管理
使用 AndPermission 2.0.2:
AndPermission.with(this)
.runtime()
.permission(Permission.CAMERA)
.onGranted { }
.onDenied { }
.start()
图片选择
使用 PictureSelector v3.11.2 + Glide 4.15.1:
PictureSelector.create(this)
.openGallery(SelectMimeType.ofImage())
.setImageEngine(GlideEngine.createGlideEngine())
.forResult { result -> }
开发规范
新增功能页面
-
创建页面结构 (按 list/details/add 组织):
page/ └── feature/ ├── list/ │ ├── FeatureListActivity.kt │ ├── FeatureListViewModel.kt │ └── FeatureListViewHolder.kt ├── details/ │ ├── FeatureDetailsActivity.kt │ └── FeatureDetailsViewModel.kt └── add/ ├── FeatureAddActivity.kt └── FeatureAddViewModel.kt -
在ARouterConstants中注册路由:
const val ACTIVITY_URL_FEATURE_LIST = "/feature/list" -
在CommonService中添加API接口:
@POST("api/feature/list") suspend fun getFeatureList(@Body data: RequestBody): BaseResultBean<List<FeatureBean>> -
创建数据Bean (在module_base/bean/):
data class FeatureBean( val id: String, val name: String // 其他字段... )
Activity开发模板
@Route(path = ARouterConstants.ACTIVITY_URL_XXX)
class XxxActivity : BaseBindingActivity<ActivityXxxBinding, XxxViewModel>() {
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开发模板
class XxxViewModel : BasePageViewModel() {
// LiveData定义
val dataList = MutableLiveData<List<DataBean>>()
fun loadData() {
launchLoadingCollect({
NetApply.api.simplePost("endpoint", params.toRequestBody())
}) {
onSuccess = { result ->
dataList.value = result.data
}
}
}
}
列表开发模板
// ViewHolder
class XxxViewHolder(itemView: View) : BaseViewHolder<XxxBean>(itemView) {
override fun onBindViewHolder(item: XxxBean, position: Int) {
// 绑定数据到视图
}
}
// Activity中使用
val adapter = CommonAdapter(
R.layout.item_xxx,
dataList
) { XxxViewHolder(it) }
binding.recyclerView.adapter = adapter
常见业务场景
扫码后查询
// 启动扫码
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")
// 调用查询接口
}
}
打印标签
// 绑定打印服务
bindService(Intent(this, PrinterService::class.java), serviceConnection, BIND_AUTO_CREATE)
// 打印
val printData = DataForSendToPrinter()
// 构建打印内容...
printerService?.sendPrintData(printData)
图片上传
// 选择图片
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<FileBean>
重要配置文件
签名配置
- 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