Files
aerologic-app/CLAUDE.md

965 lines
27 KiB
Markdown

# 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` 启动协程
- 网络请求使用 `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 <IP>:<PORT>
adb connect <IP>:<PORT>
```
---
## 详细开发指南
### 标准代码模板
#### Activity 模板
```kotlin
@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:**
```kotlin
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:**
```kotlin
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:**
```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<String>(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
<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)**
```xml
<!-- 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. 点击事件绑定**
```xml
<!-- Lambda 表达式(推荐) -->
<Button
android:onClick="@{() -> viewModel.submit()}" />
<!-- 带参数 -->
<Button
android:onClick="@{(v) -> viewModel.onItemClick(v, 1)}" />
<!-- 自定义监听器 -->
<PadSearchLayout
setOnIconClickListener="@{(v) -> viewModel.scanWaybill()}" />
```
#### DataBinding 常见错误与解决方法
**错误 1: 忘记设置 lifecycleOwner**
```kotlin
// ❌ 错误:LiveData 变化但 UI 不更新
override fun initOnCreate(savedInstanceState: Bundle?) {
binding.viewModel = viewModel
// 忘记设置 lifecycleOwner
}
// ✅ 正确:必须设置
override fun initOnCreate(savedInstanceState: Bundle?) {
binding.viewModel = viewModel
binding.lifecycleOwner = this // 关键!
}
```
**错误 2: 字符串未使用反引号**
```xml
<!-- ❌ 错误:普通引号会被识别为 XML 属性 -->
<TextView
android:text="@{"姓名:" + viewModel.name}" />
<!-- ✅ 正确:使用反引号 ` -->
<TextView
android:text="@{`姓名:` + viewModel.name}" />
```
**错误 3: 访问 LiveData 的 value 属性**
```xml
<!-- ❌ 错误:DataBinding 会自动解包,不需要 .value -->
<TextView
android:text="@{viewModel.dataBean.value.name}" />
<!-- ✅ 正确:直接访问 -->
<TextView
android:text="@{viewModel.dataBean.name}" />
```
**错误 4: 修改对象属性后 UI 不更新**
```kotlin
// ❌ 错误:修改对象内部属性,LiveData 不会触发更新
val bean = dataBean.value
bean?.name = "新名称"
// UI 不会更新,因为 LiveData 的引用没变
// ✅ 正确:重新赋值 LiveData
val bean = dataBean.value?.copy(name = "新名称")
dataBean.value = bean
```
### 核心 UI 组件详细使用
#### PadSearchLayout - 搜索输入框
```xml
<!-- 文本输入+扫码 -->
<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 - 数据展示/编辑
```xml
<!-- 文本输入 -->
<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.kt``getXxxList()`)
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注册示例:**
```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" />
```
**关键代码:**
```kotlin
// 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**
**静态启动方法:**
```kotlin
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多入口:**
```kotlin
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)
}
}
```
### 常见业务场景
#### 扫码
```kotlin
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()
}
}
```
#### 图片上传
```kotlin
val result = UploadUtil.upload(filePath)
if (result.verifySuccess()) {
val imageUrl = result.data?.newName ?: "" // 注意是newName不是url
}
```
#### 列表刷新事件
```kotlin
// 发送事件(在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()
}
```
### 常用扩展函数
```kotlin
// Toast
showToast("提示信息")
// 验证非空
if (text.verifyNullOrEmpty("请输入内容")) return
// 空处理
val text = nullableString.noNull("默认值")
// 日期格式化
val dateStr = Date().formatDate() // "2025-11-12"
// 权限申请
permission(Manifest.permission.CAMERA) { openCamera() }
```
### 常见编译错误及解决方案
#### 1. DetailsPageType 包名错误
```xml
<!-- ❌ 错误 -->
<import type="com.lukouguoji.module_base.constant.DetailsPageType" />
<!-- ✅ 正确 -->
<import type="com.lukouguoji.module_base.common.DetailsPageType" />
```
#### 2. DataLayoutType 枚举值错误
```xml
<!-- ❌ 错误: INTEGER不存在 -->
type="@{DataLayoutType.INTEGER}"
<!-- ✅ 正确: 使用INPUT -->
type="@{DataLayoutType.INPUT}"
```
**可用类型**: `INPUT` / `SPINNER` / `DATE`
#### 3. DetailsPageType 枚举值错误
```kotlin
// ❌ 错误: Edit不存在
DetailsPageType.Edit
// ✅ 正确: 使用Modify
DetailsPageType.Modify
```
**可用类型**: `Add` / `Modify` / `Details`
#### 4. IOnItemClickListener 包名错误
```kotlin
// ❌ 错误
import com.lukouguoji.module_base.impl.IOnItemClickListener
// ✅ 正确
import com.lukouguoji.module_base.interfaces.IOnItemClickListener
```
#### 5. FlowBus 使用错误
```kotlin
// ❌ 错误: 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. 图片上传字段错误
```kotlin
// ❌ 错误: UploadBean没有url字段
val imageUrl = result.data?.url
// ✅ 正确: 使用newName字段
val imageUrl = result.data?.newName
```
#### 7. pageType 必须用 LiveData
```kotlin
// ❌ 错误: DataBinding无法绑定
var pageType: DetailsPageType = DetailsPageType.Add
// ✅ 正确: 使用LiveData
val pageType = MutableLiveData(DetailsPageType.Add)
```
#### 8. RecyclerView 不支持 items 属性
```xml
<!-- ❌ 错误: items属性会导致编译错误 -->
<RecyclerView
items="@{viewModel.list}" />
<!-- ✅ 正确: 在Activity中手动更新 -->
<RecyclerView android:id="@+id/recyclerView" />
```
```kotlin
// Activity中
viewModel.list.observe(this) { data ->
binding.recyclerView.commonAdapter()?.refresh(data)
}
```
#### 9. 资源引用错误 (最常见的编译失败原因)
```xml
<!-- ❌ 错误: 引用不存在的资源会导致资源合并失败 -->
<TextView
android:background="@drawable/bg_custom"
android:textColor="@color/custom_color"
android:text="@string/custom_text" />
```
**问题原因**:
- 在布局文件中引用了项目中不存在的 `drawable``color``string` 等资源
- 导致构建时资源合并失败,无法生成R文件
- 报错信息: `Resource compilation failed``AAPT: error: resource ... not found`
**正确做法**:
1. **使用已存在的资源** - 先检查资源是否存在
```bash
# 查找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
```
2. **主动创建缺失的资源** - 如果不存在则创建
```xml
<!-- 创建 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>
```
3. **使用项目现有资源** - 避免重复创建
常用资源列表:
- **背景**: `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` 后重新构建
### 快速修复命令
```bash
# 查找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