# 导出机制
# 数据导出机制
数据导出一直是企业比较关心的问题,原先OMS支持两种数据导出方式,一种是数据量少于500条的时候直接渲染数据输出csv文件,一种是通过系统脚本上部署crontab脚本循环执行一个个导出任务。
原先的方式最大的问题就是导出数据处理的效率,以及导出数据数据量的瓶颈。
新版导出(基于3.5.0新增的任务管理服务),更大程序上的优化了这块。
# 新老版本导出比较
老版导出:
预分配5个进程,循环所有用户列表,取用户的一条任务进行执行,进程设定超时时间为30分钟,有些任务数据10w多就可能无法完成中断,或者导出任务多、或者大数据的时候导致任务处理比较慢,而且进程开的多对脚本机的负载大,有的时候进程会僵死。
新版导出:
大体上采用任务队列+多线程的技术架构。导出任务支持指定主内容导出的字段自定义以及先后顺序,并且可以选择是否需要导出明细内容,处理效率提升,处理导出任务数增加,处理的数据量增加,更加稳定高效。
# 目录结构说明
app\taoexlib\lib\ietask.php 增加系统识别的走后台导出的任务定义
app\ome\lib\export\whitelist.php 使用新版导出的相关数据定义
路径: app/ome/lib/autotask
exportsplit.php 任务切片
dataquerybysheet.php 常规分片任务查询
dataquerybyquciksheet.php 快速分片任务查询
dataquerybywhole.php 无法切片的全局查询
createfile.php 导出数据组装合并生成文件
# 逻辑梳理说明
将数据量大于一定数量满足条件的,支持后台导出的数据内容导出任务塞进队列
读取导出任务进行拆分,可拆分的根据查询条件找到记录可以唯一定位的主键数组,或者没有主键定位的用导出的总数量,根据上述两种数据进行任务的切分,一般200个切为1个小任务比较合理高效,然后根据预先判断的大于1000的进常规分片查询任务队列,小于1000的进快速分片查询任务队列,而有些不能切分的任务复杂的查询导出,走直接全局查询任务队列
读取分片任务(快/慢)以及全局查询任务队列的任务,进行结果内容的查询,并将任务保存在缓存中,当所有分片任务或整个全局查询任务结束时判断,是否都已经完成,完成的添加创建导出文件的队列任务
读取创建导出文件的组装任务,根据分片数(全局任务结果查询后也会分片保存在缓存中)组装数据在远程ftp里生成csv导出文件
# 二开及代码解读
# 增加一个导出数据模型支持后台导出
找到app\taoexlib\lib\ietask.php 文件,在$support_model变量中进行追加
var $support_model = array('omeanalysts_mdl_ome_shop','omeanalysts_mdl_ome_sales','ome_mdl_orders','omeanalysts_mdl_ome_delivery','sales_mdl_sales','wms_mdl_inventory','inventorydepth_mdl_shop_frame','omeanalysts_mdl_ome_products','omeanalysts_mdl_ome_goodsale','omeanalysts_mdl_ome_storeStatus','omeanalysts_mdl_ome_goodsrank','omeanalysts_ome_shop','omeanalysts_mdl_ome_branchdelivery','omeanalysts_mdl_ome_aftersale','ome_mdl_goods','finance_mdl_bill_order','finance_mdl_ar_statistics','omedlyexport_mdl_ome_delivery','iostock_mdl_iostocksearch','omeanalysts_mdl_ome_income','drm_mdl_distributor_product_sku','finance_mdl_analysis_bills','finance_mdl_analysis_book_bills','ome_mdl_branch_product','tgstockcost_mdl_costselect','tgstockcost_mdl_branch_product','console_mdl_branch_product','wms_mdl_branch_product','ome_mdl_reship','omeanalysts_mdl_ome_cod','invoice_mdl_order','wms_mdl_delivery','taoguaniostockorder_mdl_iso','wms_mdl_delivery_outerlogi','ome_mdl_reship_refuse','console_mdl_inventory_apply');
# 定义导出任务的处理方式和配置参数
找到app\ome\lib\export\whitelist.php文件
<?PHP
/**
* 导出白名单
*
* @author kamisama.xia@gmail.com
* @version 0.1
*/
class ome_export_whitelist
{
static public function allowed_lists($source=''){
$data_source = array(
'ome_mdl_orders' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'order_id', 'structure'=>'multi'),
'sales_mdl_sales' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'sale_id', 'structure'=>'multi'),
'ome_mdl_goods' => array('cansplit'=>0, 'splitnums'=>200),
'iostock_mdl_iostocksearch' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'iostock_id', 'structure'=>'single'),
'omedlyexport_mdl_ome_delivery' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'delivery_id', 'structure'=>'spec'),
'wms_mdl_inventory' => array('cansplit'=>0, 'splitnums'=>200),
'omeanalysts_mdl_ome_goodsale' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'item_id', 'structure'=>'single'),
'omeanalysts_mdl_ome_products' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_sales' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_aftersale' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'item_id', 'structure'=>'single'),
'omeanalysts_mdl_ome_shop' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_income' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_cod' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'delivery_id', 'structure'=>'single'),
'omeanalysts_mdl_ome_branchdelivery' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'tgstockcost_mdl_costselect' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'tgstockcost_mdl_branch_product' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_goodsrank' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_storeStatus' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'omeanalysts_mdl_ome_delivery' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'finance_mdl_bill_order' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'finance_mdl_ar_statistics' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'finance_mdl_analysis_bills' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'finance_mdl_analysis_book_bills' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'console_mdl_branch_product' => array('cansplit'=>0, 'splitnums'=>200),
'wms_mdl_branch_product' => array('cansplit'=>0, 'splitnums'=>200),
'drm_mdl_distributor_product_sku' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'inventorydepth_mdl_shop_frame' => array('cansplit'=>1, 'splitnums'=>200, 'structure'=>'single'),
'ome_mdl_reship' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'reship_id', 'structure'=>'multi'),
'invoice_mdl_order' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'id', 'structure'=>'single'),
'wms_mdl_delivery'=>array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'delivery_id', 'structure'=>'spec'),
'wms_mdl_delivery_outerlogi'=>array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'delivery_id', 'structure'=>'spec'),
'taoguaniostockorder_mdl_iso'=>array('cansplit'=>1, 'splitnums'=>200, 'primary_key'=>'iso_id', 'structure'=>'multi'),
'ome_mdl_reship_refuse' => array('cansplit'=>1, 'splitnums'=>200, 'primary_key' => 'reship_id', 'structure'=>'multi'),
);
if(!empty($source)){
return isset($data_source[$source]) ? $data_source[$source] : '';
}else{
return $data_source;
}
}
}
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
参数说明:
cansplit代表这个导出任务是否可以分片,绝大多数90%的都是可以的
splitnums 分片任务的切分数量,一般为200,合理高效
primary_key 任务分片的主键,有的话就根据主键数量切分,没的话根据count出来的结果数量切分
structure 导出数据的结构:single 单层、multi 双层、spec 特殊(发货单是比较特殊的明细和主内容在一行)
# 任务是否可拆分分片
具体根据业务数据是否有主键,按主键维度可以分片查询,比如有的数据订单,销售单,每个单据都有唯一的主键,这样的数据就可以定义为'cansplit'=>1。
这里要注意如果分片的主键无法用getList框架方法获取,需要在model层中定义方法getPrimaryIdsByCustom
有些数据因为数据是临时从多张表拼接组成或者带group by出来的,所以无法拆分,所以只能走非拆分流程
# 可拆分分片任务查询数据
一种标准的finder查询就是和列表页查询出来的内容一模一样,比如标化的订单,销售单等数据
$exportLib = kernel::single('desktop_finder_export');
$data = $exportLib->work($date_source,$params);
2
还有种就是报表用的比较多,直接自定义方法,在具体调用的数据model层中定义getExportDataByCustom方法
public function getExportDataByCustom($fields, $filter, $has_detail, $curr_sheet, $start, $end){
/*具体获取分片数据的处理代码*/
}
2
3
# 扩展导出内容
如果要扩展一个标准finder查询的主内容,可以查看订单导出中的,优惠方案的导出扩展方式,查找关键字export_extra
/**
* 订单导出列表扩展字段
*/
function export_extra_cols(){
return array(
'column_discount_plan' => array('label'=>'优惠方案','width'=>'100','func_suffix'=>'discount_plan'),
'column_mark_type_colour' => array('label'=>'订单备注图标颜色','width'=>'100','func_suffix'=>'mark_type_colour'),
);
}
/**
* 买家备注扩展字段格式化
*/
function export_extra_discount_plan($rows){
return kernel::single('ome_exportextracolumn_order_discountplan')->process($rows);
}
/**
* 订单备注图标颜色扩展字段格式化
*/
function export_extra_mark_type_colour($rows){
return kernel::single('ome_exportextracolumn_order_marktypecolour')->process($rows);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1). export_extra_cols 方法定义要扩展的字段
2). export_extra加func_suffix后缀方法,定义根据结果内容追加扩展的结果内容到结果数组中
完成以上两步即可
自定义的方法要扩展一般本身列表项就是通过get_schema自定义的,所以只要在该方面里增加字段,并且在上面的getexportdatabycustom方法里增加处理即可。
双层结构的涵明细的怎么扩展,基本就是找到查询方法自定义,finder类的标准明细内容查询是调用getexportdetail方法