From 94049f7592501621bd4f97d6f25c6ad7dd126111 Mon Sep 17 00:00:00 2001 From: p31729568 Date: Mon, 26 Aug 2019 10:13:11 +0800 Subject: [PATCH] admins: identity and professional auth feature --- app/assets/javascripts/admin.js | 27 ++++- .../javascripts/admins/common-refuse-modal.js | 54 ++++++++++ .../admins/identity_authentications/index.js | 18 ++++ .../professional_authentications/index.js | 18 ++++ .../javascripts/admins/search-form-tab.js | 10 ++ app/assets/javascripts/bootstrap.viewer.js | 50 +++++++++ app/assets/stylesheets/admin.scss | 6 +- app/assets/stylesheets/admins/common.scss | 6 ++ .../admins/identity-authentications.scss | 9 ++ .../admins/professional-authentications.scss | 9 ++ .../identity_authentications_controller.rb | 26 +++++ ...professional_authentications_controller.rb | 26 +++++ .../concerns/admins/render_helper.rb | 1 + app/helpers/admins/base_helper.rb | 102 ++++++++++++++++++ app/helpers/application_helper.rb | 101 +++-------------- app/libs/util/file_manage.rb | 40 +++++++ app/models/apply_user_authentication.rb | 6 ++ .../admins/apply_user_authentication_query.rb | 34 ++++++ .../identity_auths/agree_apply_service.rb | 38 +++++++ .../identity_auths/refuse_apply_service.rb | 40 +++++++ .../professional_auths/agree_apply_service.rb | 38 +++++++ .../refuse_apply_service.rb | 40 +++++++ .../identity_authentications/index.html.erb | 32 ++++++ .../identity_authentications/index.js.erb | 1 + .../shared/_list.html.erb | 74 +++++++++++++ .../index.html.erb | 32 ++++++ .../professional_authentications/index.js.erb | 1 + .../shared/_list.html.erb | 72 +++++++++++++ .../admins/school_statistics/index.html.erb | 4 +- .../_admin_common_refuse_modal.html.erb | 26 +++++ app/views/admins/shared/_sidebar.html.erb | 7 ++ .../admins/shared/after_render_js_hook.js.erb | 3 +- app/views/admins/shared/delete.js.erb | 2 + .../admins/users/shared/_user_list.html.erb | 22 ++-- .../apply_user_authentications/zh-CN.yml | 7 ++ config/routes.rb | 15 ++- 36 files changed, 886 insertions(+), 111 deletions(-) create mode 100644 app/assets/javascripts/admins/common-refuse-modal.js create mode 100644 app/assets/javascripts/admins/identity_authentications/index.js create mode 100644 app/assets/javascripts/admins/professional_authentications/index.js create mode 100644 app/assets/javascripts/admins/search-form-tab.js create mode 100755 app/assets/javascripts/bootstrap.viewer.js create mode 100644 app/assets/stylesheets/admins/identity-authentications.scss create mode 100644 app/assets/stylesheets/admins/professional-authentications.scss create mode 100644 app/controllers/admins/identity_authentications_controller.rb create mode 100644 app/controllers/admins/professional_authentications_controller.rb create mode 100644 app/helpers/admins/base_helper.rb create mode 100644 app/libs/util/file_manage.rb create mode 100644 app/queries/admins/apply_user_authentication_query.rb create mode 100644 app/services/admins/identity_auths/agree_apply_service.rb create mode 100644 app/services/admins/identity_auths/refuse_apply_service.rb create mode 100644 app/services/admins/professional_auths/agree_apply_service.rb create mode 100644 app/services/admins/professional_auths/refuse_apply_service.rb create mode 100644 app/views/admins/identity_authentications/index.html.erb create mode 100644 app/views/admins/identity_authentications/index.js.erb create mode 100644 app/views/admins/identity_authentications/shared/_list.html.erb create mode 100644 app/views/admins/professional_authentications/index.html.erb create mode 100644 app/views/admins/professional_authentications/index.js.erb create mode 100644 app/views/admins/professional_authentications/shared/_list.html.erb create mode 100644 app/views/admins/shared/_admin_common_refuse_modal.html.erb create mode 100644 config/locales/apply_user_authentications/zh-CN.yml diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 27844be53..72a2ff118 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -11,6 +11,7 @@ //= require select2 //= require jquery.cxselect //= require bootstrap-datepicker +//= require bootstrap.viewer //= require_tree ./i18n //= require_tree ./admins @@ -25,6 +26,9 @@ $(document).on('turbolinks:load', function(){ $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="popover"]').popover(); + // 图片查看大图 + $('img.preview-image').bootstrapViewer(); + // flash alert提示框自动关闭 if($('.admin-alert-container .alert').length > 0){ setTimeout(function(){ @@ -33,10 +37,25 @@ $(document).on('turbolinks:load', function(){ } }); -$(document).on("turbolinks:before-cache", function () { - $('[data-toggle="tooltip"]').tooltip('hide'); - $('[data-toggle="popover"]').popover('hide'); -}); +// var progressBar = new Turbolinks.ProgressBar(); + +// $(document).on('ajax:send', function(event){ +// console.log('ajax send', event); +// progressBar.setValue(0) +// progressBar.show() +// }); +// +// $(document).on('ajax:complete', function(event){ +// console.log('ajax complete', event); +// progressBar.setValue(1) +// progressBar.hide() // 分页时不触发,奇怪 +// }); +// $(document).on('ajax:success', function(event){ +// console.log('ajax success', event); +// }); +// $(document).on('ajax:error', function(event){ +// console.log('ajax error', event); +// }); $(function () { }); \ No newline at end of file diff --git a/app/assets/javascripts/admins/common-refuse-modal.js b/app/assets/javascripts/admins/common-refuse-modal.js new file mode 100644 index 000000000..5eb2f3f46 --- /dev/null +++ b/app/assets/javascripts/admins/common-refuse-modal.js @@ -0,0 +1,54 @@ +$(document).on('turbolinks:load', function() { + var $refuseModal = $('.admin-common-refuse-modal'); + if ($refuseModal.length > 0) { + var $form = $refuseModal.find('form.admin-common-refuse-form'); + var $applyIdInput = $refuseModal.find('.modal-body input[name="apply_id"]'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + reason: { + required: true, + maxlength: 200 + }, + } + }); + + // modal ready fire + $refuseModal.on('show.bs.modal', function (event) { + var $link = $(event.relatedTarget); + + var applyId = $link.data('id'); + var url = $link.data('url'); + + $applyIdInput.val(applyId); + $form.data('url', url); + }); + // modal visited fire + $refuseModal.on('shown.bs.modal', function(){ + $refuseModal.find('.modal-body input[name="reason"]').focus(); + }); + $refuseModal.on('hide.bs.modal', function () { + $applyIdInput.val(''); + $form.data('url', ''); + }) + + $refuseModal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if ($form.valid()) { + var url = $form.data('url'); + + $.ajax({ + method: 'POST', + dataType: 'script', + url: url, + data: $form.serialize(), + }).done(function(){ + $refuseModal.modal('hide'); + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/identity_authentications/index.js b/app/assets/javascripts/admins/identity_authentications/index.js new file mode 100644 index 000000000..2da644ec2 --- /dev/null +++ b/app/assets/javascripts/admins/identity_authentications/index.js @@ -0,0 +1,18 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-identity-authentications-index-page').length > 0) { + var $searchFrom = $('.identity-authentication-list-form'); + + $searchFrom.on('click', '.search-form-tab', function(){ + var $link = $(this); + + $searchFrom.find('input[name="keyword"]').val(''); + $searchFrom.find('select[name="status"]').val('processed'); + + if($link.data('value') === 'processed'){ + $searchFrom.find('.status-filter').show(); + } else { + $searchFrom.find('.status-filter').hide(); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/professional_authentications/index.js b/app/assets/javascripts/admins/professional_authentications/index.js new file mode 100644 index 000000000..769a6b2fc --- /dev/null +++ b/app/assets/javascripts/admins/professional_authentications/index.js @@ -0,0 +1,18 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-professional-authentications-index-page').length > 0) { + var $searchFrom = $('.professional-authentication-list-form'); + + $searchFrom.on('click', '.search-form-tab', function(){ + var $link = $(this); + + $searchFrom.find('input[name="keyword"]').val(''); + $searchFrom.find('select[name="status"]').val('processed'); + + if($link.data('value') === 'processed'){ + $searchFrom.find('.status-filter').show(); + } else { + $searchFrom.find('.status-filter').hide(); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/search-form-tab.js b/app/assets/javascripts/admins/search-form-tab.js new file mode 100644 index 000000000..6009412d5 --- /dev/null +++ b/app/assets/javascripts/admins/search-form-tab.js @@ -0,0 +1,10 @@ +$(document).on('turbolinks:load', function() { + var $tabs = $('.search-form-container .search-form-tabs'); + if ($tabs.length > 0) { + $tabs.on('click', '.search-form-tab', function(){ + var $activeTab = $(this); + $tabs.find('.search-form-tab').removeClass('active'); + $activeTab.addClass('active'); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/bootstrap.viewer.js b/app/assets/javascripts/bootstrap.viewer.js new file mode 100755 index 000000000..4c6aa549a --- /dev/null +++ b/app/assets/javascripts/bootstrap.viewer.js @@ -0,0 +1,50 @@ +/* + * Copyright 2018-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * WebSite: http://bootstrap-viewer.leftso.com + */ +$.fn.bootstrapViewer = function (options) { + $(this).on('click', function () { + var opts = $.extend({}, $.fn.bootstrapViewer.defaults, options); + var viewer = $(''); + $('body').append(viewer); + if ($(this).attr(opts.src)) { + $("#bootstrapViewer").find(".img-viewer").attr("src", $(this).attr(opts.src)); + $("#bootstrapViewer").modal(); + } else { + throw "图片不存在" + } + + }) + + $(this).on('mouseover', function () { + $(this).css('cursor', 'zoom-in'); + }) + +} +$.fn.bootstrapViewer.defaults = { + src: 'src' +} \ No newline at end of file diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 33d585d83..0aa1329ca 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -25,7 +25,7 @@ a { } } -input.danger { +textarea.danger, input.danger { border-color: #dc3545!important; } @@ -43,4 +43,8 @@ label.error { height: calc(1.5em + 0.75rem + 2px) } } +} + +.flex-1 { + flex: 1; } \ No newline at end of file diff --git a/app/assets/stylesheets/admins/common.scss b/app/assets/stylesheets/admins/common.scss index 9830df3c7..2254166fd 100644 --- a/app/assets/stylesheets/admins/common.scss +++ b/app/assets/stylesheets/admins/common.scss @@ -100,5 +100,11 @@ font-size: 24px; } } + + .nav-tabs { + .nav-link { + padding: 0.5rem 2rem; + } + } } diff --git a/app/assets/stylesheets/admins/identity-authentications.scss b/app/assets/stylesheets/admins/identity-authentications.scss new file mode 100644 index 000000000..417581058 --- /dev/null +++ b/app/assets/stylesheets/admins/identity-authentications.scss @@ -0,0 +1,9 @@ +.admins-identity-authentications-index-page { + .identity-authentication-list-container { + span { + &.apply-status-1 { color: #28a745; } + &.apply-status-2 { color: #dc3545; } + &.apply-status-3 { color: #6c757d; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/professional-authentications.scss b/app/assets/stylesheets/admins/professional-authentications.scss new file mode 100644 index 000000000..6ef78f92d --- /dev/null +++ b/app/assets/stylesheets/admins/professional-authentications.scss @@ -0,0 +1,9 @@ +.admins-professional-authentications-index-page { + .professional-authentication-list-container { + span { + &.apply-status-1 { color: #28a745; } + &.apply-status-2 { color: #dc3545; } + &.apply-status-3 { color: #6c757d; } + } + } +} \ No newline at end of file diff --git a/app/controllers/admins/identity_authentications_controller.rb b/app/controllers/admins/identity_authentications_controller.rb new file mode 100644 index 000000000..90758ace8 --- /dev/null +++ b/app/controllers/admins/identity_authentications_controller.rb @@ -0,0 +1,26 @@ +class Admins::IdentityAuthenticationsController < Admins::BaseController + def index + params[:status] ||= 'pending' + + applies = Admins::ApplyUserAuthenticationQuery.call(params.merge(type: 1)) + + @applies = paginate applies.preload(user: { user_extension: [:school, :department] }) + end + + def agree + Admins::IdentityAuths::AgreeApplyService.call(current_apply) + render_success_js + end + + def refuse + Admins::IdentityAuths::RefuseApplyService.call(current_apply, params) + + render_success_js + end + + private + + def current_apply + @_current_apply ||= ApplyUserAuthentication.real_name_auth.find(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/admins/professional_authentications_controller.rb b/app/controllers/admins/professional_authentications_controller.rb new file mode 100644 index 000000000..327ee696c --- /dev/null +++ b/app/controllers/admins/professional_authentications_controller.rb @@ -0,0 +1,26 @@ +class Admins::ProfessionalAuthenticationsController < Admins::BaseController + def index + params[:status] ||= 'pending' + + applies = Admins::ApplyUserAuthenticationQuery.call(params.merge(type: 2)) + + @applies = paginate applies.preload(user: { user_extension: [:school, :department] }) + end + + def agree + Admins::ProfessionalAuths::AgreeApplyService.call(current_apply) + render_success_js + end + + def refuse + Admins::ProfessionalAuths::RefuseApplyService.call(current_apply, params) + + render_success_js + end + + private + + def current_apply + @_current_apply ||= ApplyUserAuthentication.professional_auth.find(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/concerns/admins/render_helper.rb b/app/controllers/concerns/admins/render_helper.rb index c77c0ad32..3651f892b 100644 --- a/app/controllers/concerns/admins/render_helper.rb +++ b/app/controllers/concerns/admins/render_helper.rb @@ -40,6 +40,7 @@ module Admins::RenderHelper def render_delete_success render_js_template 'admins/shared/delete' end + alias_method :render_success_js, :render_delete_success def render_js_error(message) render_js_template 'admins/shared/error', locals: { message: message } diff --git a/app/helpers/admins/base_helper.rb b/app/helpers/admins/base_helper.rb new file mode 100644 index 000000000..c655be2e7 --- /dev/null +++ b/app/helpers/admins/base_helper.rb @@ -0,0 +1,102 @@ +module Admins::BaseHelper + def sidebar_item_group(url, text, **opts) + link_opts = url.start_with?('/') ? {} : { 'data-toggle': 'collapse', 'aria-expanded': false } + content = + link_to url, link_opts do + content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) + + content_tag(:span, text) + end + + content += + content_tag(:ul, id: url[1..-1], class: 'collapse list-unstyled', "data-parent": '#sidebar') do + yield + end + + raw content + end + + def sidebar_item(url, text, **opts) + content = + link_to url, 'data-controller': opts[:controller] do + content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) + + content_tag(:span, text) + end + + raw content + end + + def admin_sidebar_controller + key = params[:controller].to_s.gsub(/\//, '-') + SidebarUtil.controller_name(key) || key + end + + def define_admin_breadcrumbs(&block) + content_for(:setup_admin_breadcrumb, &block) + end + + def add_admin_breadcrumb(text, url = nil) + @_admin_breadcrumbs ||= [] + @_admin_breadcrumbs << OpenStruct.new(text: text, url: url) + end + + def display_text(str, default = '--') + str.presence || default + end + + def overflow_hidden_span(text, width: 300) + opts = { class: 'd-inline-block text-truncate', style: "max-width: #{width}px" } + opts.merge!('data-toggle': 'tooltip', title: text) if text != '--' + + content_tag(:span, text, opts) + end + + def sort_tag(content = '', **opts) + options = {} + options[:sort_by] = opts.delete(:name) + is_current_sort = params[:sort_by].to_s == options[:sort_by] + options[:sort_direction] = is_current_sort && params[:sort_direction].to_s == 'desc' ? 'asc' : 'desc' + + path = opts.delete(:path) + "?" + unsafe_params.merge(options).to_query + arrow_class = case params[:sort_direction].to_s + when 'desc' then 'fa-sort-amount-desc' + when 'asc' then 'fa-sort-amount-asc' + else '' + end + opts[:style] = "#{opts[:style]} ;position: relative;" + + content_tag(:span, opts) do + link_to path, remote: true do + content = content_tag(:span) { yield } if block_given? + + content += content_tag(:i, '', class: "fa color-light-green ml-1 #{arrow_class}", style: 'position: absolute;top:0;') if is_current_sort + raw content + end + end + end + + def javascript_void_link(name, **opts) + raw link_to(name, 'javascript:void(0)', opts) + end + + def agree_link(name, url, **opts) + klass = ['action agree-action', opts.delete(:class)].compact.join(' ') + + refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}" + url = url + (url.index('?') ? '&' : '?') + refresh_url_data + + raw link_to(name, url, { method: :post, remote: true, class: klass, 'data-confirm': '确认审核通过?'}.merge(opts)) + end + + def delete_link(name, url, **opts) + klass = ['action delete-action', opts.delete(:class)].compact.join(' ') + + refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}" + url = url + (url.index('?') ? '&' : '?') + refresh_url_data + + raw link_to(name, url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts)) + end + + def unsafe_params + params.except(:controller, :action).to_unsafe_h + end +end \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0f536433d..44169dc59 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -158,10 +158,22 @@ module ApplicationHelper disk_auth_filename('UserAuthentication', source_id, 'ID') end + def auth_file_url(source_type, source_id, type) + File.join('/images', relative_path, source_type, "#{source_id}#{type}") + end + + def real_name_auth_file_url(source_id) + auth_file_url('UserAuthentication', source_id, 'ID') + end + def disk_professional_auth_filename(source_id) disk_auth_filename('UserAuthentication', source_id, 'PRO') end + def professional_auth_file_url(source_id) + auth_file_url('UserAuthentication', source_id, 'PRO') + end + def shixun_url_to_avatar(shixun) if File.exist?(disk_filename(shixun.class, shixun.id)) File.join("images/#{relative_path}", "#{shixun.class}", "#{shixun.id}") @@ -386,95 +398,6 @@ module ApplicationHelper m_t&.include?("src=\"") ? m_t&.gsub("src=\"","src=\"#{origin_url}") : m_t end - # =========== Admin Helpers Begin =========== - def sidebar_item_group(url, text, **opts) - link_opts = url.start_with?('/') ? {} : { 'data-toggle': 'collapse', 'aria-expanded': false } - content = - link_to url, link_opts do - content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) + - content_tag(:span, text) - end - - content += - content_tag(:ul, id: url[1..-1], class: 'collapse list-unstyled', "data-parent": '#sidebar') do - yield - end - - raw content - end - - def sidebar_item(url, text, **opts) - content = - link_to url, 'data-controller': opts[:controller] do - content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) + - content_tag(:span, text) - end - - raw content - end - - def admin_sidebar_controller - key = params[:controller].to_s.gsub(/\//, '-') - SidebarUtil.controller_name(key) || key - end - - def define_admin_breadcrumbs(&block) - content_for(:setup_admin_breadcrumb, &block) - end - - def add_admin_breadcrumb(text, url = nil) - @_admin_breadcrumbs ||= [] - @_admin_breadcrumbs << OpenStruct.new(text: text, url: url) - end - - def display_text(str, default = '--') - str.presence || default - end - - def overflow_hidden_span(text, width: 300) - opts = { class: 'd-inline-block text-truncate', style: "max-width: #{width}px" } - opts.merge!('data-toggle': 'tooltip', title: text) if text != '--' - - content_tag(:span, text, opts) - end - - def sort_tag(content = '', **opts) - options = {} - options[:sort_by] = opts.delete(:name) - is_current_sort = params[:sort_by].to_s == options[:sort_by] - options[:sort_direction] = is_current_sort && params[:sort_direction].to_s == 'desc' ? 'asc' : 'desc' - - path = opts.delete(:path) + "?" + params.slice(:action, :controller).merge(options).to_unsafe_h.to_query - arrow_class = case params[:sort_direction].to_s - when 'desc' then 'fa-sort-amount-desc' - when 'asc' then 'fa-sort-amount-asc' - else '' - end - opts[:style] = "#{opts[:style]} ;position: relative;" - - content_tag(:span, opts) do - link_to path, remote: true do - content = content_tag(:span) { yield } if block_given? - - content += content_tag(:i, '', class: "fa color-light-green ml-1 #{arrow_class}", style: 'position: absolute;top:0;') if is_current_sort - raw content - end - end - end - - def javascript_void_link(name, **opts) - raw link_to(name, 'javascript:void(0)', opts) - end - - def delete_link(name, url, **opts) - klass = ['action delete-action', opts.delete(:class)].compact.join(' ') - - refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}" - url = url + (url.index('?') ? '&' : '?') + refresh_url_data - - raw link_to(name, url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts)) - end - # =========== Admin Helpers End =========== end diff --git a/app/libs/util/file_manage.rb b/app/libs/util/file_manage.rb new file mode 100644 index 000000000..620fd7f01 --- /dev/null +++ b/app/libs/util/file_manage.rb @@ -0,0 +1,40 @@ +module Util::FileManage + module_function + + # 不同的类型扩展不同的目录 + def relative_path + "avatars" + end + + def storage_path + File.join(Rails.root, "public", "images", relative_path) + end + + def disk_filename(source_type,source_id,image_file=nil) + File.join(storage_path, "#{source_type}", "#{source_id}") + end + + def disk_auth_filename(source_type, source_id, type) + File.join(storage_path, "#{source_type}", "#{source_id}#{type}") + end + + def disk_real_name_auth_filename(source_id) + disk_auth_filename('UserAuthentication', source_id, 'ID') + end + + def auth_file_url(source_type, source_id, type) + File.join('/images', relative_path, source_type, "#{source_id}#{type}") + end + + def real_name_auth_file_url(source_id) + auth_file_url('UserAuthentication', source_id, 'ID') + end + + def disk_professional_auth_filename(source_id) + disk_auth_filename('UserAuthentication', source_id, 'PRO') + end + + def professional_auth_file_url(source_id) + auth_file_url('UserAuthentication', source_id, 'PRO') + end +end \ No newline at end of file diff --git a/app/models/apply_user_authentication.rb b/app/models/apply_user_authentication.rb index d467eb850..c379fb82e 100644 --- a/app/models/apply_user_authentication.rb +++ b/app/models/apply_user_authentication.rb @@ -12,6 +12,12 @@ class ApplyUserAuthentication < ApplicationRecord after_create :send_tiding + def status_text + I18n.t!("apply_user_authentication.status.#{status}") + rescue I18n::MissingTranslationData + nil + end + private def send_tiding diff --git a/app/queries/admins/apply_user_authentication_query.rb b/app/queries/admins/apply_user_authentication_query.rb new file mode 100644 index 000000000..a4d1c2a4a --- /dev/null +++ b/app/queries/admins/apply_user_authentication_query.rb @@ -0,0 +1,34 @@ +class Admins::ApplyUserAuthenticationQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :updated_at, default_by: :updated_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + applies = ApplyUserAuthentication.where(auth_type: params[:type].presence || 1) + + status = + case params[:status] + when 'pending' then 0 + when 'processed' then [1, 2] + when 'agreed' then 1 + when 'refused' then 2 + else 0 + end + applies = applies.where(status: status) if status.present? + + # 关键字模糊查询 + keyword = params[:keyword].to_s.strip + if keyword.present? + applies = applies.joins(user: { user_extension: :school }) + .where('CONCAT(lastname,firstname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") + end + + custom_sort(applies, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/services/admins/identity_auths/agree_apply_service.rb b/app/services/admins/identity_auths/agree_apply_service.rb new file mode 100644 index 000000000..65a3b2376 --- /dev/null +++ b/app/services/admins/identity_auths/agree_apply_service.rb @@ -0,0 +1,38 @@ +class Admins::IdentityAuths::AgreeApplyService < ApplicationService + attr_reader :apply, :user + + def initialize(apply) + @apply = apply + @user = apply.user + end + + def call + ActiveRecord::Base.transaction do + apply.update!(status: 1) + user.update!(authentication: true) + + RewardGradeService.call(user, container_id: user.id, container_type: 'Authentication', score: 500) + + deal_tiding! + delete_auth_file! + end + end + + private + + def deal_tiding! + apply.tidings.where(tiding_type: 'Apply').update_all(status: 1) + + Tiding.create!(user_id: apply.user_id, trigger_user_id: 0, + container_id: apply.id, container_type: 'ApplyUserAuthentication', + belong_container_id: apply.user_id, belong_container_type: 'User', + status: 1, tiding_type: 'System') + end + + def delete_auth_file! + path = Util::FileManage.disk_real_name_auth_filename(user.id) + File.delete(path) if File.exists?(path) + + apply.update!(is_delete: true) + end +end \ No newline at end of file diff --git a/app/services/admins/identity_auths/refuse_apply_service.rb b/app/services/admins/identity_auths/refuse_apply_service.rb new file mode 100644 index 000000000..57581dd40 --- /dev/null +++ b/app/services/admins/identity_auths/refuse_apply_service.rb @@ -0,0 +1,40 @@ +class Admins::IdentityAuths::RefuseApplyService < ApplicationService + attr_reader :apply, :user, :params + + def initialize(apply, params) + @apply = apply + @user = apply.user + @params = params + end + + def call + ActiveRecord::Base.transaction do + apply.update!(status: 2, remarks: reason) + + deal_tiding! + delete_auth_file! + end + end + + private + + def reason + params[:reason].to_s.strip + end + + def deal_tiding! + apply.tidings.where(tiding_type: 'Apply').update_all(status: 1) + + Tiding.create!(user_id: apply.user_id, trigger_user_id: 0, + container_id: apply.id, container_type: 'ApplyUserAuthentication', + belong_container_id: apply.user_id, belong_container_type: 'User', + status: 2, tiding_type: 'System') + end + + def delete_auth_file! + path = Util::FileManage.disk_real_name_auth_filename(user.id) + File.delete(path) if File.exists?(path) + + apply.update!(is_delete: true) + end +end \ No newline at end of file diff --git a/app/services/admins/professional_auths/agree_apply_service.rb b/app/services/admins/professional_auths/agree_apply_service.rb new file mode 100644 index 000000000..81654f0d3 --- /dev/null +++ b/app/services/admins/professional_auths/agree_apply_service.rb @@ -0,0 +1,38 @@ +class Admins::ProfessionalAuths::AgreeApplyService < ApplicationService + attr_reader :apply, :user + + def initialize(apply) + @apply = apply + @user = apply.user + end + + def call + ActiveRecord::Base.transaction do + apply.update!(status: 1) + user.update!(professional_certification: true) + + RewardGradeService.call(user, container_id: user.id, container_type: 'Professional', score: 500) + + deal_tiding! + delete_auth_file! + end + end + + private + + def deal_tiding! + apply.tidings.where(tiding_type: 'Apply').update_all(status: 1) + + Tiding.create!(user_id: apply.user_id, trigger_user_id: 0, + container_id: apply.id, container_type: 'ApplyUserAuthentication', + belong_container_id: apply.user_id, belong_container_type: 'User', + status: 1, tiding_type: 'System') + end + + def delete_auth_file! + path = Util::FileManage.disk_professional_auth_filename(user.id) + File.delete(path) if File.exists?(path) + + apply.update!(is_delete: true) + end +end \ No newline at end of file diff --git a/app/services/admins/professional_auths/refuse_apply_service.rb b/app/services/admins/professional_auths/refuse_apply_service.rb new file mode 100644 index 000000000..b5332c999 --- /dev/null +++ b/app/services/admins/professional_auths/refuse_apply_service.rb @@ -0,0 +1,40 @@ +class Admins::ProfessionalAuths::RefuseApplyService < ApplicationService + attr_reader :apply, :user, :params + + def initialize(apply, params) + @apply = apply + @user = apply.user + @params = params + end + + def call + ActiveRecord::Base.transaction do + apply.update!(status: 2, remarks: reason) + + deal_tiding! + delete_auth_file! + end + end + + private + + def reason + params[:reason].to_s.strip + end + + def deal_tiding! + apply.tidings.where(tiding_type: 'Apply').update_all(status: 1) + + Tiding.create!(user_id: apply.user_id, trigger_user_id: 0, + container_id: apply.id, container_type: 'ApplyUserAuthentication', + belong_container_id: apply.user_id, belong_container_type: 'User', + status: 2, tiding_type: 'System') + end + + def delete_auth_file! + path = Util::FileManage.disk_professional_auth_filename(user.id) + File.delete(path) if File.exists?(path) + + apply.update!(is_delete: true) + end +end \ No newline at end of file diff --git a/app/views/admins/identity_authentications/index.html.erb b/app/views/admins/identity_authentications/index.html.erb new file mode 100644 index 000000000..170a8fc4a --- /dev/null +++ b/app/views/admins/identity_authentications/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('实名认证') %> +<% end %> + +
+ + + <%= form_tag(admins_identity_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
+ + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/identity_authentications/shared/list', locals: { applies: @applies }) %> +
+ +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/identity_authentications/index.js.erb b/app/views/admins/identity_authentications/index.js.erb new file mode 100644 index 000000000..81d108b0d --- /dev/null +++ b/app/views/admins/identity_authentications/index.js.erb @@ -0,0 +1 @@ +$('.identity-authentication-list-container').html("<%= j( render partial: 'admins/identity_authentications/shared/list', locals: { applies: @applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/identity_authentications/shared/_list.html.erb b/app/views/admins/identity_authentications/shared/_list.html.erb new file mode 100644 index 000000000..1cd849e72 --- /dev/null +++ b/app/views/admins/identity_authentications/shared/_list.html.erb @@ -0,0 +1,74 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + + <% unless is_processed %> + + <% end %> + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.user %> + + + + + + + + <% unless is_processed %> + + <% end %> + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
头像姓名身份证号学校/单位职称 + 照片 + + 时间拒绝原因状态操作
+ <%= link_to "/users/#{user.login}", class: 'identity-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %><%= user.ID_number %><%= raw [user.school_name.presence, user.department_name.presence].compact.join('
') %>
<%= user.identity %> <%= raw user.user_extension.student? && user.student_id ? "
#{user.student_id}" : '' %>
+ <% if File.exists?(disk_real_name_auth_filename(user.id)) %> + <%= image_tag(real_name_auth_file_url(user.id).to_s + "?#{Time.now.to_i}", width: 40, height: 40, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <% else %> + <%= content_tag(:span, '图片已删除', class: 'text-secondary') %> + <% end %> + <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.remarks, width: 140 %><%= apply.status_text %> + <%= agree_link '同意', agree_admins_identity_authentication_path(apply, element: ".identity-authentication-#{user.id}"), 'data-confirm': '确认审核通过?' %> + <%= javascript_void_link('拒绝', class: 'action refuse-action', + data: { + toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id, + url: refuse_admins_identity_authentication_path(apply, element: ".identity-authentication-#{user.id}") + }) %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/professional_authentications/index.html.erb b/app/views/admins/professional_authentications/index.html.erb new file mode 100644 index 000000000..e10d2bd80 --- /dev/null +++ b/app/views/admins/professional_authentications/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('职业认证') %> +<% end %> + +
+ + + <%= form_tag(admins_professional_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
+ + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/professional_authentications/shared/list', locals: { applies: @applies }) %> +
+ +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/professional_authentications/index.js.erb b/app/views/admins/professional_authentications/index.js.erb new file mode 100644 index 000000000..468c131d6 --- /dev/null +++ b/app/views/admins/professional_authentications/index.js.erb @@ -0,0 +1 @@ +$('.professional-authentication-list-container').html("<%= j( render partial: 'admins/professional_authentications/shared/list', locals: { applies: @applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/professional_authentications/shared/_list.html.erb b/app/views/admins/professional_authentications/shared/_list.html.erb new file mode 100644 index 000000000..acd62eae5 --- /dev/null +++ b/app/views/admins/professional_authentications/shared/_list.html.erb @@ -0,0 +1,72 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + <% unless is_processed %> + + <% end %> + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.user %> + + + + + + + <% unless is_processed %> + + <% end %> + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
头像姓名学校/单位职称 + 照片 + + 时间拒绝原因状态操作
+ <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %><%= raw [user.school_name.presence, user.department_name.presence].compact.join('
') %>
<%= user.identity %> <%= raw user.user_extension.student? && user.student_id ? "
#{user.student_id}" : '' %>
+ <% if File.exists?(disk_professional_auth_filename(user.id)) %> + <%= image_tag(professional_auth_file_url(user.id).to_s + "?#{Time.now.to_i}", width: 40, height: 40, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <% else %> + <%= content_tag(:span, '图片已删除', class: 'text-secondary') %> + <% end %> + <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.remarks, width: 140 %><%= apply.status_text %> + <%= agree_link '同意', agree_admins_professional_authentication_path(apply, element: ".professional-authentication-#{user.id}"), 'data-confirm': '确认审核通过?' %> + <%= javascript_void_link('拒绝', class: 'action refuse-action', + data: { + toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id, + url: refuse_admins_professional_authentication_path(apply, element: ".professional-authentication-#{user.id}") + }) %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/school_statistics/index.html.erb b/app/views/admins/school_statistics/index.html.erb index fd0c7cac1..0453dfffa 100644 --- a/app/views/admins/school_statistics/index.html.erb +++ b/app/views/admins/school_statistics/index.html.erb @@ -32,9 +32,9 @@
<%= hidden_field_tag :data_type, params[:data_type] || 'grow' %> - <%= javascript_void_link '时段对比', class: "btn btn-outline-primary btn-sm contrast-btn #{params[:data_type] == 'contrast' ? 'active' : ''}", + <%= javascript_void_link '时段对比', class: "btn btn-outline-info btn-sm contrast-btn #{params[:data_type] == 'contrast' ? 'active' : ''}", data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧分别选择需进行对比的两个时段,具体从当日5:00开始计算,下表显示两时段选定指标两时段变化情况对比' } %> - <%= javascript_void_link '数据变化', class: "btn btn-outline-primary btn-sm grow-btn #{params[:data_type] == 'contrast' ? '' : 'active'}", + <%= javascript_void_link '数据变化', class: "btn btn-outline-info btn-sm grow-btn #{params[:data_type] == 'contrast' ? '' : 'active'}", data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧选择时间段,具体从当日5:00开始计算,下表显示选定时间段内各项指标数据变化情况' } %>
diff --git a/app/views/admins/shared/_admin_common_refuse_modal.html.erb b/app/views/admins/shared/_admin_common_refuse_modal.html.erb new file mode 100644 index 000000000..a2daf7f0c --- /dev/null +++ b/app/views/admins/shared/_admin_common_refuse_modal.html.erb @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index cd64f3d0f..2be899ebd 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -37,6 +37,13 @@ <% end %> +
  • + <%= sidebar_item_group('#apply-review-submenu', '审核', icon: 'gavel') do %> +
  • <%= sidebar_item(admins_identity_authentications_path, '实名认证', icon: 'id-card-o', controller: 'admins-identity_authentications') %>
  • +
  • <%= sidebar_item(admins_professional_authentications_path, '职业认证', icon: 'drivers-license', controller: 'admins-professional_authentications') %>
  • + <% end %> + +
  • <%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %>
  • \ No newline at end of file diff --git a/app/views/admins/shared/after_render_js_hook.js.erb b/app/views/admins/shared/after_render_js_hook.js.erb index 9ceb13f5c..efc1e9295 100644 --- a/app/views/admins/shared/after_render_js_hook.js.erb +++ b/app/views/admins/shared/after_render_js_hook.js.erb @@ -1,3 +1,4 @@ ; $('[data-toggle="tooltip"]').tooltip(); -$('[data-toggle="popover"]').popover(); \ No newline at end of file +$('[data-toggle="popover"]').popover(); +$('img.preview-image').bootstrapViewer(); \ No newline at end of file diff --git a/app/views/admins/shared/delete.js.erb b/app/views/admins/shared/delete.js.erb index a10e1ae28..74512d55b 100644 --- a/app/views/admins/shared/delete.js.erb +++ b/app/views/admins/shared/delete.js.erb @@ -1,6 +1,8 @@ var deleteRow = $('<%= params[:element] %>'); var refreshUrl = '<%= params[:refresh_url] %>'; +$.notify({ message: '操作成功' },{ type: 'success' }); + var refreshFunc = function(url) { $.ajax({ url: url.length > 0 ? url : window.location.href, diff --git a/app/views/admins/users/shared/_user_list.html.erb b/app/views/admins/users/shared/_user_list.html.erb index e23e918fb..36b5d0f6e 100644 --- a/app/views/admins/users/shared/_user_list.html.erb +++ b/app/views/admins/users/shared/_user_list.html.erb @@ -1,29 +1,23 @@ - - + - - - - - + + + + + <% if users.present? %> <% users.each do |user| %> - - @@ -35,6 +29,8 @@
    ID真实姓名真实姓名 邮件地址 手机号码 单位<%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %><%= sort_tag('金币', name: 'grade', path: admins_users_path) %>操作<%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %><%= sort_tag('金币', name: 'grade', path: admins_users_path) %>操作
    + <%= link_to "/users/#{user.login}", target: '_blank' do %> - <%= overflow_hidden_span user.login, width: 100 %> - <% end %> - - <%= link_to edit_admins_user_path(user) do %> <%= overflow_hidden_span user.real_name, width: 100 %> <% end %> <%= user.experience.to_i %> <%= user.grade.to_i %> + <%= link_to '编辑', edit_admins_user_path(user), class: 'action' %> + <%= javascript_void_link('奖励', class: 'action reward-grade-action', data: { toggle: 'modal', target: '.admin-users-reward-grade-modal', id: user.id }) %> <%= javascript_void_link '解锁', class: 'action unlock-action', data: { id: user.id, confirm: '确认解锁吗?' }, style: user.locked? ? '' : 'display: none;' %> diff --git a/config/locales/apply_user_authentications/zh-CN.yml b/config/locales/apply_user_authentications/zh-CN.yml new file mode 100644 index 000000000..e886c2695 --- /dev/null +++ b/config/locales/apply_user_authentications/zh-CN.yml @@ -0,0 +1,7 @@ +zh-CN: + apply_user_authentication: + status: + '0': '待处理' + '1': '已同意' + '2': '已拒绝' + '3': '已撤销' \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 5ed8ecd69..4fc660a45 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -753,7 +753,7 @@ Rails.application.routes.draw do get :contrast, on: :collection end - resources :users, only: [:index, :edit, :update] do + resources :users, only: [:index, :edit, :update, :destroy] do member do post :reward_grade post :lock @@ -761,6 +761,19 @@ Rails.application.routes.draw do post :active end end + + resources :identity_authentications, only: [:index] do + member do + post :agree + post :refuse + end + end + resources :professional_authentications, only: [:index] do + member do + post :agree + post :refuse + end + end end #git 认证回调