使用者除了有汇出报表的需求外,也会有需要大量汇入的情境,汇入会更需要验证输入的资料,有可能是空的资料、跟预期输入 Excel 完全不同、重复的资料 (需看情境是否接受重复的资料)、部分资料输入错误...等
这边会需要使用到 roo 和 roo-xls 这 2 个 Gem ,可参考此 pr
# Gemfile
gem 'roo', '~> 2.8', '>= 2.8.3'
gem 'roo-xls', '~> 1.2'
以下范例有做简单的验证,像是判断输入的 Excel 标题列是否一样、是否有资料、是否有重复的资料、是否与 DB 资料重复
大量汇入时,可改用 activerecord-import
处理,可参考这篇文章
# app/services/shops_excel/parser.rb
module ShopsExcel
class ParseError < StandardError; end
class Parser
def execute(file_path)
list = read_excel(file_path)
sheet = list.sheet(0)
validate_title_names!(sheet)
validate_shop_names!(sheet)
process_shops(sheet)
rescue ParseError => e
subject = "[#{self.class} Error] #{e.message}, \nbacktrace: #{e.backtrace}"
Rails.logger.error subject
end
private
def read_excel(file_path)
Roo::Spreadsheet.open(file_path)
end
def validate_title_names!(sheet)
return if sheet.row(1) == ShopsExcel::Generator::TITLES
raise ParseError, '输入资料有误,比对 Excel 标头与预期不同'
end
def validate_shop_names!(sheet)
all_shop_names = sheet.column(1)[1..-1]
raise ParseError, '无资料' if all_shop_names.blank?
raise ParseError, '有重复的商家名称,请检查' if all_shop_names.uniq.size != all_shop_names.size
existed_shop_names = Shop.where(name: all_shop_names).pluck(:name)
return if existed_shop_names.blank?
raise ParseError, "有 #{existed_shop_names.size} 笔已建立过: #{existed_shop_names.join(', ')}"
end
def process_shops(sheet)
shops = []
(2..sheet.last_row).each do |index|
col_values = sheet_row(sheet, index)
shop = Shop.new(col_values)
shops << shop
end
Shop.import(shops)
end
def sheet_row(sheet, index)
{
name: sheet.row(index)[0], # 商家名称
email: sheet.row(index)[1], # 信箱
note: sheet.row(index)[2], # 备注
}
end
end
end
在 rails console
输入以下
# rails console
file = Rails.root.join("data/商家总表.xlsx").to_s
ShopsExcel::Parser.new.execute(file)
汇入要做的基本验证蛮多的,需考量各种情境,并尽可能地去预防处理,和汇出一样,建议都是放 sidekiq 处理,完成後再寄信给使用者,告知汇入结果
铁人赛文章连结:https://ithelp.ithome.com.tw/articles/10272591
medium 文章连结:https://link.medium.com/Teq2MzayRjb
本文同步发布於 小菜的 Blog https://riverye.com/
备注:之後文章修改更新,以个人部落格为主
<<: VoK Grid 各种资料型态过滤器 - day15
>>: DAY 19 『 连接 API 实作 - 天气 APP 』Part1
如果有错误,欢迎留言指教~ Q_Q 好的命名,也能够清楚的让人一看就知道是在做什麽 团队有统一的写...
什麽是 Minikube ? Minikube 是一个轻量级工具,可以看做是只有单一节点的 Kube...
鱼儿水中游 教学原文参考:鱼儿水中游 这篇文章会介绍,如何在 Scratch 3 里使用角色移动、重...
今天内容为房间载入的程序码设定,明天会教大家如何测试。 ...
触碰开灯 ( 类比讯号 ) 教学原文参考:触碰开灯 ( 类比讯号 ) 这篇文章会介绍如何使用「序列写...