Day18 - 汇入 excel-应用篇

前言

使用者除了有汇出报表的需求外,也会有需要大量汇入的情境,汇入会更需要验证输入的资料,有可能是空的资料、跟预期输入 Excel 完全不同、重复的资料 (需看情境是否接受重复的资料)、部分资料输入错误...等

实作

这边会需要使用到 rooroo-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 输入以下

范例的 Excel 档

# rails console

file = Rails.root.join("data/商家总表.xlsx").to_s
ShopsExcel::Parser.new.execute(file)

小结

汇入要做的基本验证蛮多的,需考量各种情境,并尽可能地去预防处理,和汇出一样,建议都是放 sidekiq 处理,完成後再寄信给使用者,告知汇入结果

参考资料

  1. Roo GitHub

铁人赛文章连结: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

Day 03 - 命名的规则

如果有错误,欢迎留言指教~ Q_Q 好的命名,也能够清楚的让人一看就知道是在做什麽 团队有统一的写...

[Day04]K8s Cluster环境-安装在本地端

什麽是 Minikube ? Minikube 是一个轻量级工具,可以看做是只有单一节点的 Kube...

Day 4 ( 入门 ) 鱼儿水中游

鱼儿水中游 教学原文参考:鱼儿水中游 这篇文章会介绍,如何在 Scratch 3 里使用角色移动、重...

Unity与Photon的新手相遇旅途 | Day24-Photon房间载入设定

今天内容为房间载入的程序码设定,明天会教大家如何测试。 ...

Day44 ( 电子元件 ) 触碰开灯 ( 类比讯号 )

触碰开灯 ( 类比讯号 ) 教学原文参考:触碰开灯 ( 类比讯号 ) 这篇文章会介绍如何使用「序列写...