Files
aerologic-app/CLAUDE.md

27 KiB

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 使用要点

  • 布局文件使用 <layout> 标签包裹
  • 定义 <variable> 绑定 ViewModel
  • 使用 @{} 表达式进行数据绑定
  • Activity/Fragment 中使用 DataBindingUtil 或自动生成的 Binding 类

协程使用规范

  • 在 ViewModel 中使用 viewModelScope 启动协程
  • 网络请求使用 launchCollectlaunchLoadingCollect 扩展函数
  • Flow 用于响应式数据流处理
  • 使用 withContext(Dispatchers.IO) 进行IO操作

常用构建命令

# 清理构建缓存
./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 连接问题

# 重启 ADB 服务
adb kill-server && adb start-server

# 查看设备连接状态
adb devices -l

# 无线调试 (Android 11+)
adb pair <IP>:<PORT>
adb connect <IP>:<PORT>

详细开发指南

标准代码模板

Activity 模板

@Route(path = ARouterConstants.ACTIVITY_URL_XXX)
class XxxActivity : BaseBindingActivity<ActivityXxxBinding, XxxViewModel>() {
    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:

class XxxListViewModel : BasePageViewModel() {
    val searchText = MutableLiveData<String>()
    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:

class XxxDetailsViewModel : BaseViewModel() {
    var id = ""
    val dataBean = MutableLiveData<XxxBean>()

    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:

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<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
                }
                getTopActivity().finish()
            }
        }
    }
}

DataBinding + LiveData 核心知识

最关键的设置 (最常见错误)

必须在 Activity 中设置 lifecycleOwner,否则 XML 中的 LiveData 不会自动更新 UI!

override fun initOnCreate(savedInstanceState: Bundle?) {
    setBackArrow("页面标题")
    binding.viewModel = viewModel

    // ⚠️ 关键:必须设置,否则 LiveData 无法自动更新 UI
    binding.lifecycleOwner = this
}

BaseBindingActivity 已自动设置,但如果手动使用 DataBinding 时务必记住!

XML 中 LiveData 的绑定方式

1. 单向绑定 @{}(只显示,ViewModel → UI)

<layout>
    <data>
        <variable
            name="viewModel"
            type="com.lukouguoji.xxx.XxxViewModel" />
    </data>

    <!-- LiveData 自动解包:直接访问 value -->
    <TextView
        android:text="@{viewModel.dataBean.name}" />

    <!-- 条件判断 -->
    <View
        android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" />

    <!-- 空值处理 -->
    <TextView
        android:text="@{viewModel.dataBean.name ?? `默认值`}" />

    <!-- 字符串拼接(使用反引号) -->
    <TextView
        android:text="@{`姓名:` + viewModel.dataBean.name}" />
</layout>

2. 双向绑定 @={}(可编辑,UI ↔ ViewModel)

<!-- EditText 双向绑定 -->
<EditText
    android:text="@={viewModel.searchText}" />

<!-- PadSearchLayout 双向绑定 -->
<PadSearchLayout
    type="@{SearchLayoutType.INPUT}"
    value="@={viewModel.waybillNo}" />

<!-- PadDataLayout 双向绑定 -->
<PadDataLayout
    type="@{DataLayoutType.INPUT}"
    value="@={viewModel.dataBean.name}" />

双向绑定要求:

  • 字段必须是 MutableLiveData
  • 用户输入时自动更新 ViewModel 的值
  • ViewModel 更新值时自动更新 UI

3. 点击事件绑定

<!-- Lambda 表达式(推荐) -->
<Button
    android:onClick="@{() -> viewModel.submit()}" />

<!-- 带参数 -->
<Button
    android:onClick="@{(v) -> viewModel.onItemClick(v, 1)}" />

<!-- 自定义监听器 -->
<PadSearchLayout
    setOnIconClickListener="@{(v) -> viewModel.scanWaybill()}" />

DataBinding 常见错误与解决方法

错误 1: 忘记设置 lifecycleOwner

// ❌ 错误:LiveData 变化但 UI 不更新
override fun initOnCreate(savedInstanceState: Bundle?) {
    binding.viewModel = viewModel
    // 忘记设置 lifecycleOwner
}

// ✅ 正确:必须设置
override fun initOnCreate(savedInstanceState: Bundle?) {
    binding.viewModel = viewModel
    binding.lifecycleOwner = this  // 关键!
}

错误 2: 字符串未使用反引号

<!-- ❌ 错误:普通引号会被识别为 XML 属性 -->
<TextView
    android:text="@{"姓名:" + viewModel.name}" />

<!-- ✅ 正确:使用反引号 ` -->
<TextView
    android:text="@{`姓名:` + viewModel.name}" />

错误 3: 访问 LiveData 的 value 属性

<!-- ❌ 错误:DataBinding 会自动解包,不需要 .value -->
<TextView
    android:text="@{viewModel.dataBean.value.name}" />

<!-- ✅ 正确:直接访问 -->
<TextView
    android:text="@{viewModel.dataBean.name}" />

错误 4: 修改对象属性后 UI 不更新

// ❌ 错误:修改对象内部属性,LiveData 不会触发更新
val bean = dataBean.value
bean?.name = "新名称"
// UI 不会更新,因为 LiveData 的引用没变

// ✅ 正确:重新赋值 LiveData
val bean = dataBean.value?.copy(name = "新名称")
dataBean.value = bean

核心 UI 组件详细使用

PadSearchLayout - 搜索输入框

<!-- 文本输入+扫码 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
    type="@{SearchLayoutType.INPUT}"
    value="@={viewModel.waybillNo}"
    hint="@{`请输入运单号`}"
    icon="@{@mipmap/scan_code}"
    setOnIconClickListener="@{(v)-> viewModel.scanWaybill()}" />

<!-- 日期选择 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
    type="@{SearchLayoutType.DATE}"
    value="@={viewModel.date}"
    icon="@{@mipmap/calendar}" />

<!-- 下拉选择 -->
<com.lukouguoji.module_base.ui.weight.search.layout.PadSearchLayout
    type="@{SearchLayoutType.SPINNER}"
    list="@{viewModel.statusList}"
    value="@={viewModel.status}" />

类型: INPUT / INTEGER / SPINNER / DATE

PadDataLayout - 数据展示/编辑

<!-- 文本输入 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayout
    type="@{DataLayoutType.INPUT}"
    title='@{"运单号:"}'
    titleLength="@{5}"
    value='@={viewModel.bean.waybillNo}'
    enable="@{viewModel.pageType != DetailsPageType.Details}"
    required="@{true}"
    maxLength="@{11}" />

<!-- 下拉选择 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayout
    type="@{DataLayoutType.SPINNER}"
    title='@{"状态:"}'
    list="@{viewModel.statusList}"
    value='@={viewModel.bean.status}' />

<!-- 多行输入 -->
<com.lukouguoji.module_base.ui.weight.data.layout.PadDataLayout
    type="@{DataLayoutType.INPUT}"
    inputHeight="@{100}"
    value='@={viewModel.bean.remark}' />

类型: INPUT / SPINNER / DATE 注意: 使用 PadDataLayout 时,titleLength 通常设置为 5

开发检查清单

⚠️ 重要提醒

新建 Activity 后必须在 AndroidManifest.xml 中注册,否则会报 ActivityNotFoundException 错误!

列表页开发 (8步)

  1. 创建Bean (module_base/.../bean/XxxBean.kt)
  2. 添加API接口 (Api.ktgetXxxList())
  3. 创建ViewHolder (继承BaseViewHolder)
  4. 创建ViewModel (继承BasePageViewModel)
  5. 创建Activity (继承BaseBindingActivity)
  6. 创建Layout (activity_xxx_list.xml + item_xxx.xml)
  7. 注册路由 (ARouterConstants)
  8. ⚠️ 在AndroidManifest.xml中注册Activity (app/src/main/AndroidManifest.xml)

AndroidManifest.xml注册示例:

<!-- 在app/src/main/AndroidManifest.xml的<application>标签内添加 -->
<activity
    android:name="com.lukouguoji.gnc.page.xxx.XxxActivity"
    android:configChanges="orientation|keyboardHidden"
    android:exported="false"
    android:screenOrientation="userLandscape" />

关键代码:

// Activity中绑定分页
viewModel.pageModel.bindSmartRefreshLayout(
    binding.srl, binding.recyclerView, viewModel, this
)
binding.recyclerView.addOnItemClickListener(viewModel)

详情页开发 (5步)

  1. 添加API接口 (getXxxDetails())
  2. 创建ViewModel (继承BaseViewModel)
  3. 创建Activity (含companion object静态start方法)
  4. 创建Layout
  5. ⚠️ 在AndroidManifest.xml中注册Activity

静态启动方法:

companion object {
    @JvmStatic
    fun start(context: Context, id: String) {
        val starter = Intent(context, XxxDetailsActivity::class.java)
            .putExtra(Constant.Key.ID, id)
        context.startActivity(starter)
    }
}

编辑页开发 (6步)

  1. 添加API接口 (saveXxx() + getXxxDetails())
  2. 创建ViewModel (pageType使用MutableLiveData)
  3. 创建Activity (多个静态start方法: startForAdd/Edit/Details)
  4. 创建Layout (根据pageType控制enable)
  5. FlowBus发送刷新事件
  6. ⚠️ 在AndroidManifest.xml中注册Activity

Activity多入口:

companion object {
    @JvmStatic
    fun startForAdd(context: Context) {
        val starter = Intent(context, XxxAddActivity::class.java)
            .putExtra(Constant.Key.PAGE_TYPE, DetailsPageType.Add.name)
        context.startActivity(starter)
    }

    @JvmStatic
    fun startForEdit(context: Context, id: String) {
        val starter = Intent(context, XxxAddActivity::class.java)
            .putExtra(Constant.Key.PAGE_TYPE, DetailsPageType.Modify.name)
            .putExtra(Constant.Key.ID, id)
        context.startActivity(starter)
    }
}

常见业务场景

扫码

fun scanWaybill() {
    ScanModel.startScan(getTopActivity(), Constant.RequestCode.WAYBILL)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == Constant.RequestCode.WAYBILL && resultCode == Activity.RESULT_OK) {
        waybillNo.value = data?.getStringExtra(Constant.Result.CODED_CONTENT)
        search()
    }
}

图片上传

val result = UploadUtil.upload(filePath)
if (result.verifySuccess()) {
    val imageUrl = result.data?.newName ?: ""  // 注意是newName不是url
}

列表刷新事件

// 发送事件(在ViewModel中)
viewModelScope.launch {
    FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).emit("refresh")
}

// 接收事件(在Activity中)
import com.lukouguoji.module_base.impl.observe  // 必须导入
FlowBus.with<String>(ConstantEvent.EVENT_REFRESH).observe(this) {
    viewModel.refresh()
}

常用扩展函数

// Toast
showToast("提示信息")

// 验证非空
if (text.verifyNullOrEmpty("请输入内容")) return

// 空处理
val text = nullableString.noNull("默认值")

// 日期格式化
val dateStr = Date().formatDate()  // "2025-11-12"

// 权限申请
permission(Manifest.permission.CAMERA) { openCamera() }

常见编译错误及解决方案

1. DetailsPageType 包名错误

<!-- ❌ 错误 -->
<import type="com.lukouguoji.module_base.constant.DetailsPageType" />

<!-- ✅ 正确 -->
<import type="com.lukouguoji.module_base.common.DetailsPageType" />

2. DataLayoutType 枚举值错误

<!-- ❌ 错误: INTEGER不存在 -->
type="@{DataLayoutType.INTEGER}"

<!-- ✅ 正确: 使用INPUT -->
type="@{DataLayoutType.INPUT}"

可用类型: INPUT / SPINNER / DATE

3. DetailsPageType 枚举值错误

// ❌ 错误: Edit不存在
DetailsPageType.Edit

// ✅ 正确: 使用Modify
DetailsPageType.Modify

可用类型: Add / Modify / Details

4. IOnItemClickListener 包名错误

// ❌ 错误
import com.lukouguoji.module_base.impl.IOnItemClickListener

// ✅ 正确
import com.lukouguoji.module_base.interfaces.IOnItemClickListener

5. FlowBus 使用错误

// ❌ 错误: observe需要单独导入
import com.lukouguoji.module_base.impl.FlowBus

// ✅ 正确
import com.lukouguoji.module_base.impl.FlowBus
import com.lukouguoji.module_base.impl.observe

// ❌ 错误: emit必须在协程中
FlowBus.with<String>(event).emit("data")

// ✅ 正确
viewModelScope.launch {
    FlowBus.with<String>(event).emit("data")
}

6. 图片上传字段错误

// ❌ 错误: UploadBean没有url字段
val imageUrl = result.data?.url

// ✅ 正确: 使用newName字段
val imageUrl = result.data?.newName

7. pageType 必须用 LiveData

// ❌ 错误: DataBinding无法绑定
var pageType: DetailsPageType = DetailsPageType.Add

// ✅ 正确: 使用LiveData
val pageType = MutableLiveData(DetailsPageType.Add)

8. RecyclerView 不支持 items 属性

<!-- ❌ 错误: items属性会导致编译错误 -->
<RecyclerView
    items="@{viewModel.list}" />

<!-- ✅ 正确: 在Activity中手动更新 -->
<RecyclerView android:id="@+id/recyclerView" />
// Activity中
viewModel.list.observe(this) { data ->
    binding.recyclerView.commonAdapter()?.refresh(data)
}

9. 资源引用错误 (最常见的编译失败原因)

<!-- ❌ 错误: 引用不存在的资源会导致资源合并失败 -->
<TextView
    android:background="@drawable/bg_custom"
    android:textColor="@color/custom_color"
    android:text="@string/custom_text" />

问题原因:

  • 在布局文件中引用了项目中不存在的 drawablecolorstring 等资源
  • 导致构建时资源合并失败,无法生成R文件
  • 报错信息: Resource compilation failedAAPT: error: resource ... not found

正确做法:

  1. 使用已存在的资源 - 先检查资源是否存在
# 查找drawable资源
find module_base/src/main/res/drawable -name "bg_custom*"

# 查找color定义
grep "custom_color" module_base/src/main/res/values/colors.xml

# 查找string定义
grep "custom_text" module_base/src/main/res/values/strings.xml
  1. 主动创建缺失的资源 - 如果不存在则创建
<!-- 创建 drawable: module_base/src/main/res/drawable/bg_custom.xml -->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white"/>
    <corners android:radius="4dp"/>
</shape>

<!-- 添加 color: module_base/src/main/res/values/colors.xml -->
<color name="custom_color">#333333</color>

<!-- 添加 string: module_base/src/main/res/values/strings.xml -->
<string name="custom_text">自定义文本</string>
  1. 使用项目现有资源 - 避免重复创建

常用资源列表:

  • 背景: bg_white_radius_8, bg_gray_radius_4, bg_primary_radius_4
  • 颜色: white, black, colorPrimary, text_normal, text_gray, text_red
  • 文字: 优先直接写中文字符串,少用 string 资源

错误排查流程

  1. 资源引用错误 → 检查drawable/color/string是否存在,主动创建缺失资源
  2. DataBinding错误 → 检查import包名、枚举值
  3. Unresolved reference → 检查import语句、常量定义
  4. suspend function错误 → 在viewModelScope.launch中调用
  5. 仍有问题./gradlew clean 后重新构建

快速修复命令

# 查找DetailsPageType位置
grep -r "enum class DetailsPageType" module_base/src --include="*.kt"

# 查找IOnItemClickListener位置
find module_base/src -name "IOnItemClickListener.kt"

# 查找DataLayoutType枚举值
grep -A 5 "enum class DataLayoutType" module_base/src --include="*.kt"

布局最佳实践参考

参考以下文件进行布局设计:

  • module_gjc/src/main/res/layout/activity_gjc_weighing_record_details.xml
  • module_gjc/src/main/res/layout/item_gjc_check_in_record.xml
  • module_gjc/src/main/res/layout/activity_gjc_box_weighing_details.xml
  • module_gjc/src/main/res/layout/activity_gjc_inspection.xml

开发原则

  • 资源引用必须存在 - 创建/修改布局前,确保drawable/color/string资源真实存在或主动创建
  • 必须设置 lifecycleOwner - Activity 中 binding.lifecycleOwner = this(BaseBindingActivity 已自动设置)
  • 新建Activity后必须在AndroidManifest.xml中注册
  • 优先使用项目现有基类和封装
  • 充分利用PadDataLayout和PadSearchLayout组件
  • 遵循统一命名规范
  • pageType用LiveData不用普通变量
  • XML中字符串拼接使用反引号,不访问LiveData的.value属性
  • 修改对象属性后重新赋值LiveData才能触发UI更新
  • FlowBus.emit()必须在协程中调用
  • 图片上传使用newName字段
  • RecyclerView手动更新adapter不用items属性
  • 在每个页面布局时,如有截图,务必尽可能还原图片上的页面设计,而不是推测假想。如有困难(图片看不清、不明白的地方)一律要询问,禁止自己想象。

Universal Development Guidelines

Code Quality Standards

  • Write clean, readable, and maintainable code
  • Follow consistent naming conventions across the project
  • Use meaningful variable and function names
  • Keep functions focused and single-purpose
  • Add comments for complex logic and business rules

Git Workflow

  • Use descriptive commit messages following conventional commits format
  • Create feature branches for new development
  • Keep commits atomic and focused on single changes
  • Use pull requests for code review before merging
  • Maintain a clean commit history

Documentation

  • Keep README.md files up to date
  • Document public APIs and interfaces
  • Include usage examples for complex features
  • Maintain inline code documentation
  • Update documentation when making changes

Testing Approach

  • Write tests for new features and bug fixes
  • Maintain good test coverage
  • Use descriptive test names that explain the expected behavior
  • Organize tests logically by feature or module
  • Run tests before committing changes

Security Best Practices

  • Never commit sensitive information (API keys, passwords, tokens)
  • Use environment variables for configuration
  • Validate input data and sanitize outputs
  • Follow principle of least privilege
  • Keep dependencies updated

Project Structure Guidelines

File Organization

  • Group related files in logical directories
  • Use consistent file and folder naming conventions
  • Separate source code from configuration files
  • Keep build artifacts out of version control
  • Organize assets and resources appropriately

Configuration Management

  • Use configuration files for environment-specific settings
  • Centralize configuration in dedicated files
  • Use environment variables for sensitive or environment-specific data
  • Document configuration options and their purposes
  • Provide example configuration files

Development Workflow

Before Starting Work

  1. Pull latest changes from main branch
  2. Create a new feature branch
  3. Review existing code and architecture
  4. Plan the implementation approach

During Development

  1. Make incremental commits with clear messages
  2. Run tests frequently to catch issues early
  3. Follow established coding standards
  4. Update documentation as needed

Before Submitting

  1. Run full test suite
  2. Check code quality and formatting
  3. Update documentation if necessary
  4. Create clear pull request description

Common Patterns

Error Handling

  • Use appropriate error handling mechanisms for the language
  • Provide meaningful error messages
  • Log errors appropriately for debugging
  • Handle edge cases gracefully
  • Don't expose sensitive information in error messages

Performance Considerations

  • Profile code for performance bottlenecks
  • Optimize database queries and API calls
  • Use caching where appropriate
  • Consider memory usage and resource management
  • Monitor and measure performance metrics

Code Reusability

  • Extract common functionality into reusable modules
  • Use dependency injection for better testability
  • Create utility functions for repeated operations
  • Design interfaces for extensibility
  • Follow DRY (Don't Repeat Yourself) principle

Review Checklist

Before marking any task as complete:

  • Code follows established conventions
  • Tests are written and passing
  • Documentation is updated
  • Security considerations are addressed
  • Performance impact is considered
  • Code is reviewed for maintainability