# 数据库开发完整指南

# 📋 概述

本指南涵盖了 OMS 系统中数据库开发的完整流程,包括数据库结构文件(dbschema)格式、数据库更新机制、文件命名规范等内容。

适用范围:所有OMS系统开发人员
重要性:⭐⭐⭐⭐⭐(必须掌握)

# 🚨 重要提醒

数据库结构文件是 PHP 格式,不是 XML 格式!

# 📋 基本格式

# 文件位置

app/{应用名}/dbschema/{表名}.php
1

# 文件结构

<?php
/**
 * {表注释}
 */

$db['{表名}'] = array(
    'columns' => array(
        // 字段定义
    ),
    'index' => array(
        // 索引定义
    ),
    'comment' => '{表注释}',
    'engine' => 'innodb',
    'version' => '$Rev: $',
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 📝 字段定义

# 基本字段格式

'字段名' => array(
    'type' => '字段类型',
    'required' => true/false,
    'default' => '默认值',
    'label' => '字段标签',
    'width' => 宽度,
    'in_list' => true/false,
    'default_in_list' => true/false,
    'comment' => '字段注释',
),
1
2
3
4
5
6
7
8
9
10

# 常用字段类型

# 1. 整数类型

// 自增主键
'id' => array(
    'type' => 'int unsigned',
    'required' => true,
    'pkey' => true,
    'extra' => 'auto_increment',
    'label' => 'ID',
),

// 普通整数
'quantity' => array(
    'type' => 'int',
    'default' => 0,
    'label' => '数量',
),

// 小整数
'status' => array(
    'type' => 'tinyint(1)',
    'default' => 1,
    'label' => '状态',
),

// 大整数
'amount' => array(
    'type' => 'bigint',
    'default' => 0,
    'label' => '金额',
),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 2. 字符串类型

// varchar
'name' => array(
    'type' => 'varchar(100)',
    'required' => true,
    'label' => '名称',
),

// text
'description' => array(
    'type' => 'text',
    'label' => '描述',
),

// char
'code' => array(
    'type' => 'char(10)',
    'label' => '编码',
),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3. 小数类型

'price' => array(
    'type' => 'decimal(20,3)',
    'required' => true,
    'label' => '价格',
),
1
2
3
4
5

# 4. 布尔类型

'is_active' => array(
    'type' => 'bool',
    'default' => 'false',
    'label' => '是否启用',
),
1
2
3
4
5

# 5. 时间类型

// 时间戳
'created' => array(
    'type' => 'time',
    'default' => 0,
    'label' => '创建时间',
),

// 自动更新的时间
'last_modified' => array(
    'type' => 'last_modify',
    'label' => '最后更新时间',
),

// TIMESTAMP 类型
'at_time' => array(
    'type' => 'TIMESTAMP',
    'label' => '创建时间',
    'default' => 'CURRENT_TIMESTAMP',
),

// TIMESTAMP 自动更新
'up_time' => array(
    'type' => 'TIMESTAMP',
    'label' => '更新时间',
    'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 6. 关联表类型

// 关联其他表
'cat_id' => array(
    'type' => 'table:basic_material_cat@material',
    'required' => false,
    'default' => 0,
    'label' => '分类',
),
1
2
3
4
5
6
7

# 🔑 索引定义

# 普通索引

'index' => array(
    'ind_name' => array(
        'columns' => array(
            0 => 'column_name',
        ),
    ),
),
1
2
3
4
5
6
7

# 唯一索引

'index' => array(
    'uni_name' => array(
        'columns' => array(
            0 => 'column_name',
        ),
        'prefix' => 'UNIQUE',
    ),
),
1
2
3
4
5
6
7
8

# 复合索引

'index' => array(
    'ind_multi' => array(
        'columns' => array(
            0 => 'column1',
            1 => 'column2',
        ),
    ),
),
1
2
3
4
5
6
7
8

# 📄 完整示例

# 示例1:商品分类表

<?php
/**
 * 商品分类数据结构
 */

$db['goods_category'] = array(
    'columns' => array(
        'category_id' => array(
            'type' => 'int unsigned',
            'required' => true,
            'pkey' => true,
            'extra' => 'auto_increment',
            'label' => '分类ID',
            'width' => 80,
            'in_list' => true,
            'default_in_list' => true,
        ),
        'name' => array(
            'type' => 'varchar(100)',
            'required' => true,
            'label' => '分类名称',
            'width' => 200,
            'in_list' => true,
            'default_in_list' => true,
        ),
        'parent_id' => array(
            'type' => 'int unsigned',
            'default' => 0,
            'label' => '父分类ID',
            'width' => 80,
        ),
        'sort_order' => array(
            'type' => 'int unsigned',
            'default' => 0,
            'label' => '排序',
            'width' => 80,
            'in_list' => true,
        ),
        'status' => array(
            'type' => 'varchar(10)',
            'default' => 'enabled',
            'label' => '状态',
            'width' => 80,
            'in_list' => true,
            'default_in_list' => true,
        ),
        'created' => array(
            'type' => 'time',
            'default' => 0,
            'label' => '创建时间',
            'width' => 150,
            'in_list' => true,
        ),
        'last_modified' => array(
            'type' => 'last_modify',
            'label' => '最后更新时间',
            'width' => 150,
            'in_list' => true,
            'default_in_list' => true,
        ),
    ),
    'index' => array(
        'ind_parent_id' => array(
            'columns' => array(
                0 => 'parent_id',
            ),
        ),
        'ind_status' => array(
            'columns' => array(
                0 => 'status',
            ),
        ),
    ),
    'comment' => '商品分类表',
    'engine' => 'innodb',
    'version' => '$Rev: $',
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# 示例2:经销商品价格表

<?php
/**
 * 经销商品价格数据结构
 */

$db['goods_price'] = array(
    'columns' => array(
        'id' => array(
            'type' => 'int',
            'required' => true,
            'pkey' => true,
            'extra' => 'auto_increment',
            'label' => 'ID',
            'in_list' => true,
            'default_in_list' => true,
            'width' => 50,
        ),
        'bs_id' => array(
            'type' => 'table:business@dealer',
            'required' => true,
            'label' => '经销商编码',
            'width' => 120,
            'in_list' => true,
            'default_in_list' => true,
            'order' => 10,
        ),
        'bm_id' => array(
            'type' => 'table:basic_material@material',
            'required' => true,
            'label' => '基础物料编码',
            'width' => 120,
            'in_list' => true,
            'default_in_list' => true,
            'order' => 20,
        ),
        'price' => array(
            'type' => 'decimal(20,3)',
            'required' => true,
            'label' => '采购价',
            'in_list' => true,
            'default_in_list' => true,
            'order' => 30,
        ),
        'price_unit' => array(
            'type' => 'varchar(10)',
            'label' => '价格单位',
            'in_list' => true,
            'default_in_list' => true,
            'order' => 35,
            'default' => '',
        ),
        'start_time' => array(
            'type' => 'time',
            'label' => '生效时间',
            'in_list' => true,
            'default_in_list' => true,
            'filtertype' => 'date',
            'filterdefault' => true,
            'order' => 40,
        ),
        'end_time' => array(
            'type' => 'time',
            'label' => '过期时间',
            'in_list' => true,
            'default_in_list' => true,
            'filtertype' => 'date',
            'filterdefault' => true,
            'order' => 50,
        ),
        'at_time' => array(
            'type' => 'TIMESTAMP',
            'label' => '创建时间',
            'default' => 'CURRENT_TIMESTAMP',
            'width' => 150,
            'in_list' => true,
            'default_in_list' => true,
            'order' => 60,
        ),
        'up_time' => array(
            'type' => 'TIMESTAMP',
            'label' => '更新时间',
            'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
            'width' => 150,
            'in_list' => true,
            'default_in_list' => true,
            'order' => 70,
        ),
    ),
    'index' => array(
        'ind_bs_bm' => array(
            'columns' => array(
                0 => 'bs_id',
                1 => 'bm_id',
            ),
        ),
        'ind_bs_id' => array(
            'columns' => array(
                0 => 'bs_id',
            ),
        ),
        'ind_bm_id' => array(
            'columns' => array(
                0 => 'bm_id',
            ),
        ),
    ),
    'comment' => '经销商品价格管理',
    'engine' => 'innodb',
    'version' => '$Rev: $',
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

# 🎯 字段属性说明

# 列表展示相关

'in_list' => true,              // 是否在列表中显示
'default_in_list' => true,      // 默认是否显示
'width' => 120,                 // 列宽度
'order' => 10,                  // 显示顺序
'hidden' => true,               // 是否隐藏
1
2
3
4
5

# 筛选相关

'filtertype' => 'normal',       // 筛选类型
'filterdefault' => true,        // 默认是否显示筛选
'searchtype' => 'has',          // 搜索类型
1
2
3

# 编辑相关

'editable' => false,            // 是否可编辑
'required' => true,             // 是否必填
'is_title' => true,             // 是否是标题字段
1
2
3

# 筛选类型(filtertype)

  • normal - 普通下拉选择
  • fuzzy_search - 模糊搜索(单选)
  • fuzzy_search_multiple - 模糊搜索(多选)
  • date - 日期范围选择
  • time - 时间范围选择
  • yes - 是/否选择
  • textarea - 文本域输入

# 搜索类型(searchtype)

  • has - 包含搜索
  • head - 前缀搜索
  • equal - 精确匹配
  • nequal - 不等于

# ⚠️ 常见错误

# 错误1:使用XML格式

❌ 错误:
app/material/dbschema/goods_category.xml

✅ 正确:
app/material/dbschema/goods_category.php
1
2
3
4
5

# 错误2:忘记执行更新

❌ 错误:
创建文件后不执行 php cmd update

✅ 正确:
创建或修改 dbschema 文件后立即执行 php cmd update
1
2
3
4
5

# 🔄 数据库更新机制

# ⚠️ 重要原则

只有在修改了数据库结构定义或XML配置文件时,才需要执行update命令。

纯PHP代码逻辑的修改不需要执行update命令,修改后直接生效。

# 需要执行update的情况

# 1. 修改了dbschema文件

当你修改了任何 app/[应用名]/dbschema/*.php 文件时:

具体场景:

  • ✅ 新增表字段
  • ✅ 修改字段类型、长度、默认值
  • ✅ 删除表字段
  • ✅ 新增、修改、删除索引
  • ✅ 修改表注释
  • ✅ 新增数据表文件
  • ✅ 修改字段的 in_listdefault_in_listorder 等配置

示例:

// 修改了这样的字段定义就需要update
'price' => array(
    'type'            => 'decimal(20,3)',  // 修改了类型
    'required'        => true,
    'label'           => '采购价格',        // 修改了标签
    'in_list'         => true,             // 修改了列表显示
    'default_in_list' => true,
    'order'           => 30,               // 修改了排序
),
1
2
3
4
5
6
7
8
9

# 2. 修改了XML配置文件

当你修改了任何XML配置文件时:

具体场景:

  • app/[应用名]/desktop.xml - 菜单、权限配置
  • app/[应用名]/services.xml - 服务注册配置
  • app/[应用名]/app.xml - 应用配置
  • ✅ 新增菜单项
  • ✅ 修改菜单权限
  • ✅ 新增权限定义
  • ✅ 修改服务类注册

示例:

<!-- 修改了这样的配置就需要update -->
<menu controller='admin_goods_price' action='index' 
      permission='dealer_goods_price' display='true' order='6001040'>
    经销商品价格管理
</menu>

<permission id="dealer_goods_price" show='ome_roles:show_dealer'>
    经销商品价格管理
</permission>

<service id="desktop_finder.dealer_mdl_goods_price">
    <class>dealer_finder_goods_price</class>
</service>
1
2
3
4
5
6
7
8
9
10
11
12
13

# ❌ 不需要执行update的情况

# 1. 修改PHP代码文件

  • app/[应用名]/model/*.php - 模型文件
  • app/[应用名]/controller/*.php - 控制器文件
  • app/[应用名]/lib/*.php - 业务逻辑文件
  • app/[应用名]/view/*.html - 视图模板文件

# 2. 修改文档文件

  • docs/*.md - 文档文件
  • README.md - 说明文件

# 3. 纯逻辑修改

  • ❌ Bug修复
  • ❌ 功能优化
  • ❌ 代码重构
  • ❌ 新增方法和函数
  • ❌ 修改业务逻辑

示例(这些修改不需要update):

// 修改了模型中的方法逻辑
public function modifier_start_time($data)
{
    return $data ? date('Y-m-d H:i:s', $data) : '';  // 修改了格式化逻辑
}

// 新增了验证方法
public function validatePrice($price) 
{
    return is_numeric($price) && $price >= 0;  // 新增方法
}

// 修改了控制器逻辑
public function exportTemplate()
{
    $data = array(
        array('经销商编码', '基础物料编码', '采购价'),  // 修改了模板数据
    );
    // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 🔧 执行update命令

# 命令格式

# 方式1:通过docker执行(推荐)
docker exec -it php82 bash -c "cd /var/www/html/erp/oms-yjdf && php app/base/cmd update"

# 方式2:进入容器后执行
docker exec -it php82 bash
cd /var/www/html/erp/oms-yjdf
php app/base/cmd update
1
2
3
4
5
6
7

# 执行时机

  • ✅ 完成dbschema文件修改后立即执行
  • ✅ 完成XML配置文件修改后立即执行
  • ✅ 部署到新环境时执行
  • ❌ 每次修改PHP代码后不需要执行

# 执行结果

成功执行后会看到类似输出:

Scanning local Applications... ok.
Updating base_application_dbtable@dealer.
Applications database and services is up-to-date, ok.
1
2
3

# 📝 总结

记住这个简单的判断规则:

  • 改数据库结构 = 需要update
  • 改XML配置 = 需要update
  • 改PHP代码 = 不需要update
  • 改文档 = 不需要update

当不确定时,可以先尝试不执行update,如果功能不正常再考虑是否需要update。


# 错误3:字段类型错误

❌ 错误:
'created' => array(
    'type' => 'datetime',  // 不支持这个类型
),

✅ 正确:
'created' => array(
    'type' => 'time',      // 使用 time 类型
    'default' => 0,
),
1
2
3
4
5
6
7
8
9
10

# 错误4:索引命名不规范

❌ 错误:
'index' => array(
    'parent_id' => array(  // 直接用字段名
        ...
    ),
),

✅ 正确:
'index' => array(
    'ind_parent_id' => array(  // 使用 ind_ 前缀
        ...
    ),
),
1
2
3
4
5
6
7
8
9
10
11
12
13

# 📚 参考资料

# 实际案例

  • app/material/dbschema/basic_material.php - 复杂字段示例
  • app/dealer/dbschema/goods_price.php - 关联表示例
  • app/ome/dbschema/orders.php - 大表结构示例

# 📁 文件命名规范

# Model文件命名规则

在OMS系统中,Model文件的命名遵循特定的目录结构规则:

# 基本规则

对于数据表 sdb_[应用名]_[表名],对应的Model文件路径和类名:

文件路径app/[应用名]/model/[表名去掉应用名前缀].php
类名[应用名]_mdl_[表名去掉应用名前缀]

# 具体示例

  1. 简单表名

    数据表:sdb_dealer_goods_price
    文件路径:app/dealer/model/goods/price.php
    类名:dealer_mdl_goods_price
    
    1
    2
    3
  2. 带ops后缀的关联表

    数据表:sdb_organization_organization_ops
    文件路径:app/organization/model/organization/ops.php
    类名:organization_mdl_organization_ops
    
    1
    2
    3
  3. 其他复合表名

    数据表:sdb_ome_group_ops
    文件路径:app/ome/model/group/ops.php
    类名:ome_mdl_group_ops
    
    1
    2
    3

# 目录结构规则

  • 如果表名包含多个下划线分隔的部分,需要创建对应的子目录
  • 最后一个部分作为文件名(.php)
  • 前面的部分作为目录路径

# 常见错误示例

❌ 错误:app/organization/model/organization_ops.php
✅ 正确:app/organization/model/organization/ops.php

❌ 错误:app/dealer/model/goods_price.php  
✅ 正确:app/dealer/model/goods/price.php
1
2
3
4
5

重要提醒:违反命名规则会导致 Call to undefined method 错误,因为框架的自动加载机制无法找到正确的Model文件。

# Lib文件命名规则

在OMS系统中,Lib文件的命名也遵循特定的目录结构规则:

# 基本规则

对于 kernel::single('[应用名]_[目录名]_[文件名]'),对应的Lib文件路径和类名:

文件路径app/[应用名]/lib/[目录名]/[文件名].php
类名[应用名]_[目录名]_[文件名]

# 具体示例

  1. 简单Lib文件

    调用:kernel::single('organization_organization_permission')
    文件路径:app/organization/lib/organization/permission.php
    类名:organization_organization_permission
    
    1
    2
    3
  2. 多层目录结构

    调用:kernel::single('ome_finder_extend_filter_branch_product')
    文件路径:app/ome/lib/finder/extend/filter/branch/product.php
    类名:ome_finder_extend_filter_branch_product
    
    1
    2
    3
  3. 业务逻辑Lib

    调用:kernel::single('console_receipt_inventory')
    文件路径:app/console/lib/receipt/inventory.php
    类名:console_receipt_inventory
    
    1
    2
    3

# 目录结构规则

  • 下划线分隔的部分对应目录结构
  • 最后一个部分作为文件名(.php)
  • 前面的部分作为目录路径

# 常见错误示例

❌ 错误:app/organization/lib/organization_permission.php
✅ 正确:app/organization/lib/organization/permission.php

❌ 错误:app/ome/lib/finder_extend_filter_branch_product.php
✅ 正确:app/ome/lib/finder/extend/filter/branch/product.php
1
2
3
4
5

重要提醒:违反命名规则会导致 Class not found 错误,因为框架的自动加载机制无法找到正确的Lib文件。


# 📋 前端开发规范

# 下拉列表搜索实现标准

在OMS系统中,下拉列表默认都应该支持搜索功能,使用以下标准实现方式:

# 基本模板

<{input type='select' issearch="fuzzy-search" required="required" name='field_name' options=$optionsData}>
1

# JavaScript初始化

tail.select('select[issearch="fuzzy-search"]', {
    search: true,
    width: 300,
    height: 300,
    searchMinLength: 0
});
1
2
3
4
5
6

# PHP数据格式

// 使用array_column生成options格式
$data = $model->getList('id,name', $filter);
$this->pagedata['optionsData'] = array_column($data, 'name', 'id');
1
2
3

# 参数说明

  • issearch="fuzzy-search" - 启用模糊搜索
  • search: true - 开启搜索功能
  • width: 300 - 下拉框宽度
  • height: 300 - 下拉框高度
  • searchMinLength: 0 - 最小搜索长度(0表示无限制)

# 使用场景

  • ✅ 门店选择下拉框
  • ✅ 仓库选择下拉框
  • ✅ 商品选择下拉框
  • ✅ 用户选择下拉框
  • ✅ 任何需要从大量选项中快速定位的场景

# 注意事项

  • 数据量较大时,建议添加适当的过滤条件
  • 搜索功能对用户体验提升明显,特别是选项超过10个时
  • 保持与系统其他页面的一致性

# 📚 开发流程建议

# 1. 新功能开发流程

1. 设计数据表结构 → 创建dbschema文件 → 执行update
2. 配置菜单权限 → 修改desktop.xml → 执行update  
3. 注册服务类 → 修改services.xml → 执行update
4. 开发业务逻辑 → 编写model/controller/lib → 不需要update
5. 测试功能 → 修改bug → 不需要update
1
2
3
4
5

# 2. 功能修改流程

1. 如果需要修改表结构 → 修改dbschema → 执行update
2. 如果需要修改菜单权限 → 修改desktop.xml → 执行update
3. 如果只是修改业务逻辑 → 修改PHP代码 → 不需要update
1
2
3

# ⚠️ 注意事项

# 1. 数据安全

  • 在生产环境执行update前务必备份数据库
  • 新增字段建议设置合理的默认值
  • 删除字段前确认不会影响现有功能

# 2. 团队协作

  • 修改dbschema或XML文件后及时通知团队成员
  • 提交代码时在提交信息中标明是否需要执行update
  • 代码评审时重点检查数据库结构变更

# 3. 错误处理

如果update执行失败:

  1. 检查语法错误(XML格式、PHP语法)
  2. 检查权限问题
  3. 检查数据库连接
  4. 查看错误日志定位问题

# 📚 参考资料

# 实际案例

  • app/material/dbschema/basic_material.php - 复杂字段示例
  • app/dealer/dbschema/goods_price.php - 关联表示例
  • app/ome/dbschema/orders.php - 大表结构示例

# 相关文档


最后更新:2025年 版本:1.0

最后更新: 11/11/2025, 9:12:34 PM