From 77620f77358a402ed257dd144055b5ed512c3079 Mon Sep 17 00:00:00 2001 From: yystopf Date: Mon, 16 May 2022 16:59:25 +0800 Subject: [PATCH] add: download and import user template xlsx --- .../admins/import_users_controller.rb | 2 +- .../admins/new_import_user_from_excel.rb | 15 ++++ .../admins/import_user_from_excel_service.rb | 71 ++++++++++++++++++ app/views/admins/users/index.html.erb | 4 +- public/导入用户模板.xlsx | Bin 0 -> 9230 bytes 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 app/imports/admins/new_import_user_from_excel.rb create mode 100644 app/services/admins/import_user_from_excel_service.rb create mode 100644 public/导入用户模板.xlsx diff --git a/app/controllers/admins/import_users_controller.rb b/app/controllers/admins/import_users_controller.rb index 5d8a60ce6..4575b08d5 100644 --- a/app/controllers/admins/import_users_controller.rb +++ b/app/controllers/admins/import_users_controller.rb @@ -2,7 +2,7 @@ class Admins::ImportUsersController < Admins::BaseController def create return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile) - result = Admins::ImportUserService.call(params[:file].to_io) + result = Admins::ImportUserFromExcelService.call(params[:file].to_io) render_ok(result) rescue Admins::ImportUserService::Error => ex render_error(ex) diff --git a/app/imports/admins/new_import_user_from_excel.rb b/app/imports/admins/new_import_user_from_excel.rb new file mode 100644 index 000000000..b9a452cf1 --- /dev/null +++ b/app/imports/admins/new_import_user_from_excel.rb @@ -0,0 +1,15 @@ +class Admins::NewImportUserFromExcel < BaseImportXlsx + UserData = Struct.new(:login, :email, :password, :nickname) + + def read_each(&block) + sheet.each_row_streaming(pad_cells: true, offset: 1) do |row| + data = row.map(&method(:cell_value))[0..3] + block.call UserData.new(*data) + end + end + + private + def cell_value(obj) + obj&.cell_value + end +end \ No newline at end of file diff --git a/app/services/admins/import_user_from_excel_service.rb b/app/services/admins/import_user_from_excel_service.rb new file mode 100644 index 000000000..709551c23 --- /dev/null +++ b/app/services/admins/import_user_from_excel_service.rb @@ -0,0 +1,71 @@ +class Admins::ImportUserFromExcelService < ApplicationService + Error = Class.new(StandardError) + + attr_reader :file, :result + + def initialize(file) + @file = file + @result = { success: 0, fail: [] } + end + + def call + raise Error, '文件不存在' if file.blank? + excel = Admins::NewImportUserFromExcel.new(file) + + excel.read_each(&method(:save_user)) + result + rescue ApplicationImport::Error => ex + raise Error, ex.message + end + + private + def save_user(data) + user = find_user(data) + if user.blank? + create_user(data) + result[:success] +=1 + else + fail_data = data.as_json + fail_data[:data] = fail_data.values.join(",") + fail_data[:message] = '用户已存在' + result[:fail] << fail_data + end + + rescue Exception => ex + fail_data = data.as_json + fail_data[:data] = fail_data.values.join(",") + fail_data[:message] = ex.message + result[:fail] << fail_data + end + + def create_user(data) + ActiveRecord::Base.transaction do + username = data.login&.gsub(/\s+/, "") + email = data.email&.gsub(/\s+/, "") + password = data.password + nickname = data.nickname&.gsub(/\s+/, "") + raise Error, "无法使用以下关键词:#{username},请重新命名" if ReversedKeyword.check_exists?(data.login) + Register::RemoteForm.new({username: username, email: email, password: password, platform: 'forge'}).validate! + user = User.new(admin: false, login: username, mail: email, nickname: nickname, platform: 'forge' , type: "User") + user.password = password + user.activate + raise Error, user.errors.full_messages.join(",") unless user.valid? + interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) + if interactor.success? + gitea_user = interactor.result + result = Gitea::User::GenerateTokenService.call(username, password) + user.gitea_token = result['sha1'] + user.gitea_uid = gitea_user[:body]['id'] + UserExtension.create!(user_id: user.id) if user.save! + else + raise interactor.error, 'gitea user create error' + end + + user + end + end + + def find_user(data) + User.find_by(login: data.login) + end +end \ No newline at end of file diff --git a/app/views/admins/users/index.html.erb b/app/views/admins/users/index.html.erb index e21a3d665..2f41ffdb6 100644 --- a/app/views/admins/users/index.html.erb +++ b/app/views/admins/users/index.html.erb @@ -31,7 +31,9 @@ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> - <%= javascript_void_link '导入用户', class: 'btn btn-secondary btn-sm', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> + <%= link_to '下载导入模板', "/导入用户模板.xlsx", class: 'btn btn-secondary mr-3' %> + + <%= javascript_void_link '导入用户', class: 'btn btn-secondary', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> diff --git a/public/导入用户模板.xlsx b/public/导入用户模板.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c538bed63aab06831ab7286e6a1ffe52a0337864 GIT binary patch literal 9230 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 literal 0 HcmV?d00001