Files
aerologic-app/CLAUDE.md
2025-11-10 18:21:19 +08:00

11 KiB
Raw Blame History

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. 依赖下载问题解决:

  2. 配置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

打印流程:

  1. 启动PrinterService
  2. 扫描/绑定蓝牙打印机
  3. 构建打印数据(DataForSendToPrinter)
  4. 发送打印任务

扫码功能

  • 使用 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 -> }

开发规范

新增功能页面

  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中注册路由:

    const val ACTIVITY_URL_FEATURE_LIST = "/feature/list"
    
  3. 在CommonService中添加API接口:

    @POST("api/feature/list")
    suspend fun getFeatureList(@Body data: RequestBody): BaseResultBean<List<FeatureBean>>
    
  4. 创建数据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.ktConstantEvent.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