ネットショップ移行CSVツール開発

ネットショップコンサルの会社さんからの依頼で制作したGolang製ツール

Page content

ネットショップコンサルの会社さんからの依頼で制作したショップ移行CSVツールの開発についてまとめる。
やりたい事は現サイトの商品画像URLが記載された「商品CSV」を活用して移行先の商品一括登録に必要な全ての「商品画像」をコピー保存して、「商品登録CSV」も作成すること。

背景

  • ECモールからSaaS型ECサイトへの移行
  • 今後商品数の多いサイトの移行があった場合に商品画像を手動で保存するのが厳しい
  • 移行元サイトの商品画像はファイル名が不規則かつJPEG、GIFが混在している場合がある
  • 移行先サイトは特定SaaSで商品画像は商品と紐づけるためにファイル名に商品番号を付与したJPEGというルールがある

機能

  • CSVアップロード
  • 商品CSV検証、エラー検出
  • JPEG画像保存
  • 商品登録CSV作成
  • Zip作成(商品画像、商品登録CSV)
  • Zipダウンロード

技術

  • プログラミング言語 golang, JavaScript
  • Webフレームワーク gin
  • サーバー heroku
  • ファイルストレージ Amazon S3
  • DB Heroku Postgres

画面仕様

  1. CSVファイル添付後に「アップロード」ボタンを押下で処理開始。
  2. CSVに不正があればエラーメッセージ(内容、行番号)を表示して終了。
  3. CSVが妥当であれば「処理中…」画面で待機。
  4. 処理完了後に自動でダウンロード開始。

インプット

  • 移行元ネットショップ用の商品CSV

アウトプット

  • 移行元ネットショップの商品画像をまとめたZipファイル
  • 移行先ネットショップ用の商品登録CSV

実装

golangを使った理由

  • 想定する商品数が500個として各商品の画像が20個とすると10,000個で結構な保存数になるので並列処理を使いたい。

herokuを使った理由

  • git push heroku master コマンド1発で更新反映ができる
  • golangでサクッとアプリ開発できる

商品画像が多い場合の対応

問題発生
  • 開発中に商品数の多いCSVでテストすると画像処理に30秒以上かかりタイムアウトするようになった。
func main() {
    router := gin.Default()

    router.POST("/upload", func(c *gin.Context) {
        
        // 1.CSV解析処理
        
        // 以下の2,3の処理が画像が多い場合に30秒で完了しなくなった
        // 2.画像保存処理
        // 3.商品登録CSV
        // 4. 2,3をZIPにしてtmpディレクトリに保存
        
        // 5.ZIPダウンロード
        c.File(zipFilePath)
    })
}
改善
  • CSV解析後の画像処理を非同期処理に変更
  • 非同期処理にするとtmpフォルダでは永続化が危ういのでAmazon S3という外部ストレージに保存。
  • JavaScriptで定期的に保存完了チェックを行い、完了したタイミングでダウンロード処理にアクセスする。
func main() {
    router := gin.Default()

    router.POST("/upload", func(c *gin.Context) {
        
        // 1.CSV解析処理
        
        // 非同期処理
        go func() {
            // 2.画像保存処理
            // 3.商品登録CSV
            // 4. 2,3をZIPにしてAmazon S3に保存
        }()
        
        // 2, 3, 4が完了しなくてもすぐ返す
        c.JSON(200, gin.H{"params": params})
    })
    
    router.GET("/check", func(c *gin.Context) {
        // 4が完了しているかチェック
        c.JSON(200, gin.H{"check": false})
    })
 
    router.GET("/download", func(c *gin.Context) {
        // 4をダウンロード
        c.File(filePath)
    })	
}

顧客との打ち合わせ

  • 当初インプットとして「現行の商品CSV」でなく「現行サイトのURL」を想定してしまった。
  • そのため「現行サイトをスクレイピングをしてimgタグを抽出して…」というこちらのミスリードがあった。
  • 現システムでのデータ源は何ですか?と質問すれば回避されると思われる。
  • ある程度の規模のショッピングサイトでDB、CSVのどちらもないということはまず無い。

顧客ニーズ

  • アルバイト、パートさんを雇って手動で1週間かかるような作業時間もシステム化することで自動で20分に短縮できる。
  • 仮に指示ミスがあっても自動化されていれば手戻りは最小限で済む。
  • 顧客は手動での作業経験があったため強めのウォンツを感じた。
  • ニーズはシステム移行に関わらず「システム連携のデータ作業」の省力化と確実性にあると考える。