diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 14d59af77..e0508efe8 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -1,5 +1,5 @@ class AccountsController < ApplicationController - before_action :require_login, only: [:login_check, :simple_update] + before_action :require_login, only: [:login_check, :simple_update, :change_password] include ApplicationHelper #skip_before_action :check_account, :only => [:logout] @@ -224,6 +224,7 @@ class AccountsController < ApplicationController def change_password return render_error("两次输入的密码不一致") if params[:password].to_s != params[:new_password_repeat].to_s @user = User.find_by(login: params[:login]) + return render_forbidden unless User.current.login == @user&.login return render_error("此用户禁止修改密码!") if @user.id.to_i === 104691 return render_error("未找到相关用户!") if @user.blank? return render_error("旧密码不正确") unless @user.check_password?(params[:old_password]) diff --git a/app/controllers/admins/projects_controller.rb b/app/controllers/admins/projects_controller.rb index f5f210a0d..79582cb7c 100644 --- a/app/controllers/admins/projects_controller.rb +++ b/app/controllers/admins/projects_controller.rb @@ -6,7 +6,7 @@ class Admins::ProjectsController < Admins::BaseController sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on' sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' search = params[:search].to_s.strip - projects = Project.where("name like ? OR identifier LIKE ?", "%#{search}%", "%#{search}%").order("#{sort_by} #{sort_direction}") + projects = Project.where("id = ? OR name like ? OR identifier LIKE ?", search, "%#{search}%", "%#{search}%").order("#{sort_by} #{sort_direction}") case params[:category] when 'public' projects = projects.where(is_public: true) diff --git a/app/controllers/admins/user_actions_controller.rb b/app/controllers/admins/user_actions_controller.rb new file mode 100644 index 000000000..ff52d5a1a --- /dev/null +++ b/app/controllers/admins/user_actions_controller.rb @@ -0,0 +1,14 @@ +class Admins::UserActionsController < Admins::BaseController + before_action :require_admin + + def index + @user_actions = UserAction.order(created_at: :desc) + @user_actions = @user_actions.where(action_type: params[:action_type]) if params[:action_type].present? + keyword = params[:keyword].to_s.strip.presence + if keyword + sql = 'login LIKE :keyword OR phone LIKE :keyword OR email LIKE :keyword' + @user_actions = @user_actions.where(sql, keyword: "%#{keyword}%") + end + @user_actions = paginate @user_actions + end +end diff --git a/app/controllers/admins/users_controller.rb b/app/controllers/admins/users_controller.rb index e544cfb8e..f552c9cd1 100644 --- a/app/controllers/admins/users_controller.rb +++ b/app/controllers/admins/users_controller.rb @@ -27,10 +27,30 @@ class Admins::UsersController < Admins::BaseController def destroy UserAction.create(action_id: @user.id, action_type: "DestroyUser", user_id: current_user.id, :ip => request.remote_ip, data_bank: @user.attributes.to_json) - @user.destroy! - Gitea::User::DeleteService.call(@user.login) - - render_delete_success + # org_ids = TeamUser.where(user_id: @user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @user.id).pluck(:organization_id) + # organizations = Organization.where(id: org_ids) + # organizations.each do |org| + # # org.team_users.joins(:team).where(user_id: @user.id, teams: {authorize: %w(owner)}) + # owner_count = org.team_users.joins(:team).where(teams: {authorize: %w(owner)}).count + # # 多个owner时,仅将用户从组织移除, 一个时直接删除 + # if owner_count > 1 + # org.team_users.joins(:team).where(user_id: @user.id, teams: {authorize: %w(owner)}).destroy_all + # org.organization_users.where(user_id: @user.id, organization_id: org.id).destroy_all + # else + # org.destroy + # end + # end + # @user.destroy! + # Gitea::User::DeleteService.call(@user.login, true) + # + # render_delete_success + + @result_object = Api::V1::Users::DeleteUserService.call(@user) + if @result_object + render_delete_success + else + render_js_error('删除失败!') + end end def lock diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index 47087c523..09c5a3276 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -115,4 +115,28 @@ class Api::V1::UsersController < Api::V1::BaseController return render_error('更改手机号失败!') end end + + + def check_user_can_delete + org_ids = TeamUser.where(user_id: @observe_user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @observe_user.id).pluck(:organization_id) + org_count = TeamUser.where(organization_id: org_ids).where(user_id: @observe_user.id).joins(:team).where(teams: {authorize: %w(owner)}).count + project_count = Project.where(user_id: @observe_user.id).count + render_ok({ can_delete: org_count == 0 && project_count == 0, org_count: org_count, project_count: project_count }) + end + + + def destroy + return tip_exception(-1, "密码不正确.") unless @observe_user.check_password?(params[:password]) + org_ids = TeamUser.where(user_id: @observe_user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @observe_user.id).pluck(:organization_id) + org_count = TeamUser.where(organization_id: org_ids).where(user_id: @observe_user.id).joins(:team).where(teams: {authorize: %w(owner)}).count + project_count = Project.where(user_id: @observe_user.id).count + return tip_exception(-1, "当前账号名下存在拥有的组织/代码库,请先删除或转让后再尝试注销操作.") if org_count > 0 || project_count > 0 + UserAction.create(action_id: @observe_user.id, action_type: "DestroyUser", user_id: @observe_user.id, :ip => request.remote_ip, data_bank: @observe_user.attributes.to_json, memo: params[:memo]) + @result_object = Api::V1::Users::DeleteUserService.call(@observe_user) + if @result_object + return render_ok + else + return render_error('删除失败!') + end + end end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 52387cbbd..d9dd9e2e6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -376,7 +376,7 @@ class ApplicationController < ActionController::Base # 多浏览器退出账号时,token不存在处理 if current_domain_session && autologin_user.nil? autologin_user = (User.active.find(current_domain_session) rescue nil) - set_autologin_cookie(autologin_user) + set_autologin_cookie(autologin_user) if autologin_user.present? end autologin_user end diff --git a/app/controllers/concerns/login_helper.rb b/app/controllers/concerns/login_helper.rb index c0e8d01c0..c3b3ecc92 100644 --- a/app/controllers/concerns/login_helper.rb +++ b/app/controllers/concerns/login_helper.rb @@ -73,6 +73,17 @@ module LoginHelper session[:"#{default_yun_session}"] = nil end + def clear_user_cookie + if edu_setting('cookie_domain').present? + cookies.delete(autologin_cookie_name, domain: edu_setting('cookie_domain')) + else + cookies.delete(autologin_cookie_name) + end + # 清除前端写入的用户名 + Rails.logger.info("########________cookies['login']___________###########{cookies['login']}") + cookies.delete("login") + end + # Sets the logged in user def logged_user=(user) reset_session diff --git a/app/controllers/owners_controller.rb b/app/controllers/owners_controller.rb index 28a5210d5..ad3a70423 100644 --- a/app/controllers/owners_controller.rb +++ b/app/controllers/owners_controller.rb @@ -11,7 +11,10 @@ class OwnersController < ApplicationController end def show - @owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id]) + # login = params[:id].to_s[0..-6] + login = params[:id].to_s + @owner = Owner.find_by(login: login) || Owner.find_by(id: login) + clear_user_cookie unless @owner.present? return render_not_found unless @owner.present? # 组织 if @owner.is_a?(Organization) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 202bae458..4724224e8 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -253,6 +253,7 @@ class ProjectsController < ApplicationController def destroy if current_user.admin? || @project.manager?(current_user) ActiveRecord::Base.transaction do + UserAction.create(action_id: @project.id, action_type: "DestroyProject", user_id: current_user.id, :ip => request.remote_ip, data_bank: @project.attributes.to_json) close_fork_pull_requests_by(@project) Gitea::Repository::DeleteService.new(@project.owner, @project.identifier,current_user.gitea_token).call @project.destroy! @@ -365,7 +366,7 @@ class ProjectsController < ApplicationController if @project_detail.save! attachment_ids = Array(params[:attachment_ids]) logger.info "=============> #{Array(params[:attachment_ids])}" - @attachments = Attachment.where(id: attachment_ids) + @attachments = Attachment.where(id: attachment_ids).or(Attachment.where(uuid: attachment_ids)) @attachments.update_all( container_id: @project_detail.id, container_type: @project_detail.model_name.name, diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 27895a758..761a88fa8 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -714,6 +714,7 @@ class UsersController < ApplicationController private def load_user @user = User.find_by_login(params[:id]) || User.find_by(id: params[:id]) + clear_user_cookie unless @user.present? end def user_params diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d670e9a0d..bab9ee9b2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -146,13 +146,13 @@ module ApplicationHelper # 用户图像url,如果不存在的话,source为匿名用户,即默认使用匿名用户图像 def url_to_avatar(source) if File.exist?(disk_filename(source&.class, source&.id)) - ctime = File.ctime(disk_filename(source.class, source.id)).to_i - if %w(User Organization).include?(source.class.to_s) - File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}" + ctime = File.ctime(disk_filename(source&.class, source&.id)).to_i + if %w(User Organization).include?(source&.class.to_s) + File.join("images", relative_path, ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}" else - File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}" + File.join("images/avatars", ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}" end - elsif source.class.to_s == 'User' + elsif source&.class.to_s == 'User' source.get_letter_avatar_url end end diff --git a/app/helpers/avatar_helper.rb b/app/helpers/avatar_helper.rb index b703e1b4e..ad0ec0cde 100644 --- a/app/helpers/avatar_helper.rb +++ b/app/helpers/avatar_helper.rb @@ -13,13 +13,13 @@ module AvatarHelper def url_to_avatar(source) if File.exist?(disk_filename(source&.class, source&.id)) - ctime = File.ctime(disk_filename(source.class, source.id)).to_i - if %w(User Organization).include?(source.class.to_s) - File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}" + ctime = File.ctime(disk_filename(source&.class, source&.id)).to_i + if %w(User Organization).include?(source&.class.to_s) + File.join("images", relative_path, ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}" else - File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}" + File.join("images/avatars", ["#{source&.class}", "#{source&.id}"]) + "?t=#{ctime}" end - elsif source.class.to_s == 'User' + elsif source&.class.to_s == 'User' source.get_letter_avatar_url end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 0f34b7c1b..80cfa92e0 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -129,7 +129,7 @@ module ProjectsHelper end def jianmu_devops_url - EduSetting.get("jianmu_devops_url") || "https://ci-v3.test.jianmuhub.com" + EduSetting.get("jianmu_devops_url") end diff --git a/app/jobs/open_project_dev_ops_job.rb b/app/jobs/open_project_dev_ops_job.rb index b3fe99e6f..c5d68a861 100644 --- a/app/jobs/open_project_dev_ops_job.rb +++ b/app/jobs/open_project_dev_ops_job.rb @@ -6,6 +6,7 @@ class OpenProjectDevOpsJob < ApplicationJob def perform(project_id, user_id) project = Project.find_by(id: project_id) return if project.blank? + return if EduSetting.get("jianmu_devops_url").blank? user = User.find_by(id: user_id) code = jianmu_devops_code(project, user) uri = URI.parse("#{jianmu_devops_url}/activate?code=#{URI.encode_www_form_component(code)}") diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 26b4ce2c6..8d276625e 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -34,7 +34,7 @@ class PullRequest < ApplicationRecord belongs_to :issue belongs_to :user - belongs_to :project, counter_cache: true, touch: true + belongs_to :project, counter_cache: true, touch: true, optional: true belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id, optional: true has_many :pull_request_assigns, foreign_key: :pull_request_id has_many :pull_request_tags, foreign_key: :pull_request_id diff --git a/app/models/user.rb b/app/models/user.rb index 988f6d8c2..1fee098fb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -137,7 +137,7 @@ class User < Owner has_many :tidings, :dependent => :destroy # has_many :journals_for_messages, :as => :jour, :dependent => :destroy - has_many :attachments,foreign_key: :author_id, :dependent => :destroy + has_many :attachments,foreign_key: :author_id has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', dependent: :destroy @@ -160,7 +160,7 @@ class User < Owner # 教学案例 # has_many :libraries, dependent: :destroy - has_many :project_trends, dependent: :destroy + has_many :project_trends has_many :oauths , dependent: :destroy has_many :organization_users, dependent: :destroy @@ -168,8 +168,8 @@ class User < Owner has_many :pinned_projects, dependent: :destroy has_many :is_pinned_projects, through: :pinned_projects, source: :project accepts_nested_attributes_for :is_pinned_projects - has_many :issues, dependent: :destroy, foreign_key: :author_id - has_many :pull_requests, dependent: :destroy + has_many :issues, foreign_key: :author_id + has_many :pull_requests has_many :public_keys, class_name: "Gitea::PublicKey",primary_key: :gitea_uid, foreign_key: :owner_id, dependent: :destroy has_one :user_template_message_setting, dependent: :destroy diff --git a/app/models/user_action.rb b/app/models/user_action.rb index b4173fe77..289fa188e 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -10,6 +10,10 @@ # updated_at :datetime not null # ip :string(255) # data_bank :text(65535) +# login :string(255) +# phone :string(255) +# email :string(255) +# memo :text(65535) # # Indexes # @@ -19,5 +23,76 @@ # index_user_actions_on_user_id (user_id) # -class UserAction < ApplicationRecord -end +class UserAction < ApplicationRecord + + after_save :add_user_info + + serialize :data_bank, JSON + + def action_name + case action_type + when "DestroyUser" then "用户注销" + when "DestroyProject" then "删除项目" + when "Login" then "登录" + when "Logout" then "退出登录" + else self.action_type + end + end + + def opt_user_name + user = User.find_by(id: self.user_id) + if user.present? + user&.login + else + del_user = UserAction.find_by(action_type: "DestroyUser", action_id: self.user_id) + del_user.present? ? del_user.user.login : "不存在用户:#{user_id}" + end + end + + def action_info + case action_type + when "DestroyUser" then "账号:#{user&.login}
邮箱:#{user&.mail}
手机号:#{user&.phone}
昵称:#{user&.nickname}
" + when "DestroyProject" then "项目名称:#{project&.name}
项目标识:#{project&.identifier}
" + else "--" + end + end + + def user + action_user = if action_type == "DestroyUser" && data_bank.present? + build_mode("User") + else + User.find_by(id: self.user_id) + end + action_user + end + + def project + action_project = if action_type == "DestroyProject" && data_bank.present? + build_mode("Project") + else + Project.find_by(id: self.action_id) + end + action_project + end + def build_mode(model_name) + model = model_name.constantize.new + model_name.constantize.column_names.each do |col| + data = self.data_bank.class == String ? JSON.parse(self.data_bank) : self.data_bank + model[col] = data[col] + end + model + rescue =>err + return nil + end + + private + def add_user_info + if self.login.blank? + if user.present? + self.login = user.login + self.email = user.mail + self.phone = user.phone + end + end + end +end diff --git a/app/queries/application_query.rb b/app/queries/application_query.rb index e2d7c446f..4d43bfeb6 100644 --- a/app/queries/application_query.rb +++ b/app/queries/application_query.rb @@ -24,6 +24,26 @@ class ApplicationQuery end end + # find one repo that a user has tokens + def find_one_balance_with_token(user_id, project_id) + # return 3 statuses: UnknownErr/ResUserNotExisted/Success + params = { + "request-type": "query user balance of single repo", + "username": user_id.to_s, + "token_name": project_id.to_s + }.to_json + Rails.logger.info "query user balance of single repo params======= #{params}" + resp_body = Blockchain::InvokeBlockchainApi.call(params) + Rails.logger.info "resp_body======= #{resp_body}" + if resp_body['status'] == 0 + return resp_body + elsif resp_body['status'] == 100 + return 0 # 找不到用户返回0 + else + raise "区块链接口请求失败." + end + end + # find one repo that a user has tokens def find_one_balance(user_id, project_id) diff --git a/app/queries/blockchain/balance_query.rb b/app/queries/blockchain/balance_query.rb index 045d2fe8a..d50b38ee6 100644 --- a/app/queries/blockchain/balance_query.rb +++ b/app/queries/blockchain/balance_query.rb @@ -11,9 +11,15 @@ class Blockchain::BalanceQuery < ApplicationQuery if is_current_admin_user result_list = [] if params[:project_id].present? or params[:keyword].present? - project_ids = params[:project_id].present? ? [params[:project_id]] : Project.where(user_id: params["user_id"]).like(params[:keyword]).ids + token_list, total_count = find_repo_with_token(params["user_id"], 1, 10000) + p_ids = [] + token_list.each do |t| + p_ids.push(t['token_name'].to_i) + end + project_ids = params[:project_id].present? ? [params[:project_id]] : Project.where(id: p_ids).like(params[:keyword]).ids project_ids.each do |project_id| - project_balance = find_one_balance(params["user_id"], project_id) + project_balance = find_one_balance_with_token(params["user_id"], project_id) + next if project_balance == 0 project = Project.find_by(id: project_balance['token_name'].to_i) if project.present? owner = User.find_by(id: project.user_id) diff --git a/app/services/api/pm/issues/update_service.rb b/app/services/api/pm/issues/update_service.rb index 78d62fb2b..661bd211f 100644 --- a/app/services/api/pm/issues/update_service.rb +++ b/app/services/api/pm/issues/update_service.rb @@ -64,7 +64,6 @@ class Api::Pm::Issues::UpdateService < ApplicationService build_assigner_issue_journal_details unless assigner_ids.nil?# 操作记录 build_attachment_issue_journal_details unless attachment_ids.nil? build_issue_tag_issue_journal_details unless issue_tag_ids.nil? - build_issue_project_trends if status_id.present? # 开关时间记录 build_assigner_participants unless assigner_ids.nil? # 负责人 build_edit_participants build_atme_participants if @atme_receivers.present? @@ -92,6 +91,7 @@ class Api::Pm::Issues::UpdateService < ApplicationService build_after_issue_journal_details if @updated_issue.previous_changes.present? # 操作记录 build_previous_issue_changes + build_issue_project_trends if status_id.present? # 开关时间记录 build_cirle_blockchain_token if blockchain_token_num.present? unless @project.id.zero? # @信息发送 @@ -172,7 +172,7 @@ class Api::Pm::Issues::UpdateService < ApplicationService def build_issue_project_trends if @updated_issue.previous_changes["status_id"].present? && @updated_issue.previous_changes["status_id"][1] == 5 - @updated_issue.project_trends.new({user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE}) + @updated_issue.project_trends.create!({user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE}) end if @updated_issue.previous_changes["status_id"].present? && @updated_issue.previous_changes["status_id"][0] == 5 @updated_issue.project_trends.where(action_type: ProjectTrend::CLOSE).each(&:destroy!) diff --git a/app/services/api/v1/issues/update_service.rb b/app/services/api/v1/issues/update_service.rb index 5f040b25f..10f814300 100644 --- a/app/services/api/v1/issues/update_service.rb +++ b/app/services/api/v1/issues/update_service.rb @@ -64,7 +64,6 @@ class Api::V1::Issues::UpdateService < ApplicationService build_assigner_issue_journal_details unless assigner_ids.nil?# 操作记录 build_attachment_issue_journal_details unless attachment_ids.nil? build_issue_tag_issue_journal_details unless issue_tag_ids.nil? - build_issue_project_trends if status_id.present? # 开关时间记录 build_assigner_participants unless assigner_ids.nil? # 负责人 build_edit_participants build_atme_participants if @atme_receivers.present? @@ -92,6 +91,7 @@ class Api::V1::Issues::UpdateService < ApplicationService build_after_issue_journal_details if @updated_issue.previous_changes.present? # 操作记录 build_previous_issue_changes + build_issue_project_trends if status_id.present? # 开关时间记录 build_cirle_blockchain_token if blockchain_token_num.present? unless @project.id.zero? # @信息发送 @@ -172,7 +172,7 @@ class Api::V1::Issues::UpdateService < ApplicationService def build_issue_project_trends if @updated_issue.previous_changes["status_id"].present? && @updated_issue.previous_changes["status_id"][1] == 5 - @updated_issue.project_trends.new({user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE}) + @updated_issue.project_trends.create!({user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE}) end if @updated_issue.previous_changes["status_id"].present? && @updated_issue.previous_changes["status_id"][0] == 5 @updated_issue.project_trends.where(action_type: ProjectTrend::CLOSE).each(&:destroy!) diff --git a/app/services/api/v1/users/delete_user_service.rb b/app/services/api/v1/users/delete_user_service.rb new file mode 100644 index 000000000..12775eac3 --- /dev/null +++ b/app/services/api/v1/users/delete_user_service.rb @@ -0,0 +1,38 @@ +class Api::V1::Users::DeleteUserService < ApplicationService + attr_reader :user + def initialize(user) + @user = user + end + + def call + begin + ActiveRecord::Base.transaction do + org_ids = TeamUser.where(user_id: @user.id).pluck(:organization_id) | OrganizationUser.where(user_id: @user.id).pluck(:organization_id) + organizations = Organization.where(id: org_ids) + organizations.each do |org| + # org.team_users.joins(:team).where(user_id: @user.id, teams: {authorize: %w(owner)}) + owner_count = org.team_users.joins(:team).where(teams: {authorize: %w(owner)}).count + # 多个owner时,仅将用户从组织移除, 一个时直接删除 + org.team_users.where(user_id: @user.id).destroy_all + org.organization_users.where(user_id: @user.id, organization_id: org.id).destroy_all + if owner_count == 1 + if org.team_users.joins(:team).where(user_id: @user.id, teams: { authorize: %w(owner) }).count > 0 + org.destroy! + end + end + end + @user.destroy! + del_user_data_by_sql(@user.id) + Gitea::User::DeleteService.call(@user.login, true) + end + return true + rescue + raise Error, "服务器错误,请联系系统管理员!" + end + end + + def del_user_data_by_sql(user_id) + sql1 = "delete from memos where author_id=#{user_id}" + ActiveRecord::Base.connection.execute(sql1) + end +end \ No newline at end of file diff --git a/app/services/cache/v2/project_common_service.rb b/app/services/cache/v2/project_common_service.rb index 1e85f3e08..98a19a80d 100644 --- a/app/services/cache/v2/project_common_service.rb +++ b/app/services/cache/v2/project_common_service.rb @@ -102,7 +102,7 @@ class Cache::V2::ProjectCommonService < ApplicationService return else load_project - return unless @project.is_full_public + return unless @project.present? && @project.is_full_public if @owner_id.present? if $redis_cache.hget(project_common_key, owner_id_key).nil? reset_project_owner_id diff --git a/app/services/gitea/user/delete_service.rb b/app/services/gitea/user/delete_service.rb index 9011158d2..ce357e3d5 100644 --- a/app/services/gitea/user/delete_service.rb +++ b/app/services/gitea/user/delete_service.rb @@ -1,8 +1,9 @@ class Gitea::User::DeleteService < Gitea::ClientService - attr_reader :username + attr_reader :username, :purge - def initialize(username) + def initialize(username, purge = false) @username = username + @purge = purge end def call @@ -20,7 +21,7 @@ class Gitea::User::DeleteService < Gitea::ClientService end def request_url - "/admin/users/#{username}" + @purge ? "/admin/users/#{username}?purge=true" : "/admin/users/#{username}" end def params diff --git a/app/views/admins/edu_settings/_list.html.erb b/app/views/admins/edu_settings/_list.html.erb index a37cc9bef..5dc0e1483 100644 --- a/app/views/admins/edu_settings/_list.html.erb +++ b/app/views/admins/edu_settings/_list.html.erb @@ -17,7 +17,7 @@ <%= edu_setting.name %> - <%= edu_setting.value %> + <%= edu_setting.value %> <%= overflow_hidden_span display_text(edu_setting.description), width: 200 %> <%= edu_setting.created_at&.strftime('%Y-%m-%d %H:%M') %> diff --git a/app/views/admins/projects/index.html.erb b/app/views/admins/projects/index.html.erb index be36229fd..bf37a7cb5 100644 --- a/app/views/admins/projects/index.html.erb +++ b/app/views/admins/projects/index.html.erb @@ -4,7 +4,7 @@
<%= form_tag(admins_projects_path, method: :get, class: 'form-inline search-form flex-1', id: 'project-list-form', remote: true) do %> - <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '项目名称/标识检索') %> + <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '项目ID/项目名称/标识检索') %> <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index 7919b12d0..d48db392d 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -26,6 +26,7 @@
  • <%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs', has_permission: current_user.admin? || current_user.business?) %>
  • <%= sidebar_item(admins_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps', has_permission: current_user.admin? || current_user.business?) %>
  • <%= sidebar_item(admins_feedbacks_path, '用户反馈', icon: 'question-circle', controller: 'admins-feedbacks', has_permission: current_user.admin? || current_user.business?) %>
  • +
  • <%= sidebar_item(admins_user_actions_path, '操作记录', icon: 'pencil-square', controller: 'admins-user_actions', has_permission: current_user.admin?) %>
  • <%= sidebar_item(admins_system_notifications_path, '系统公告配置', icon: 'bell', controller: 'admins-system_notifications', has_permission: current_user.admin? || current_user.business?) %>
  • <% end %> diff --git a/app/views/admins/user_actions/_user_action_list.html.erb b/app/views/admins/user_actions/_user_action_list.html.erb new file mode 100644 index 000000000..2df720b60 --- /dev/null +++ b/app/views/admins/user_actions/_user_action_list.html.erb @@ -0,0 +1,35 @@ + + + + + + + + + + + + + <% if user_actions.present? %> + <% user_actions.each_with_index do |action, index| %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    序号操作类型操作人账号关联基本信息操作时间原因/备注
    <%= list_index_no((params[:page] || 1).to_i, index) %><%= action.action_name %> + <%= link_to "/#{action.user&.login}", target: '_blank' do %> + <%= overflow_hidden_span action.opt_user_name, width: 100 %> + <% end %> + <%= raw action.action_info %> + <%= display_text(action.created_at&.strftime('%Y-%m-%d %H:%M')) %><%= action.memo %>
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: user_actions } %> diff --git a/app/views/admins/user_actions/index.html.erb b/app/views/admins/user_actions/index.html.erb new file mode 100644 index 000000000..ae62414c8 --- /dev/null +++ b/app/views/admins/user_actions/index.html.erb @@ -0,0 +1,33 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('操作记录', admins_user_actions_path) %> +<% end %> + +
    + <%= form_tag(admins_user_actions_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> + 操作类型: + <% action_type_options = [['自定义',''],['用户注销','DestroyUser'], ['删除项目', 'DestroyProject']] %> + <%= select_tag(:action_type_select, options_for_select(action_type_options), class: 'form-control') %> + <%= text_field_tag(:action_type, params[:action_type], class: 'form-control col-sm-2 ml-3',style: 'display:;', placeholder: '自定义操作类型检索') %> + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '操作人用户名/邮箱/手机号检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + +
    + +
    + <%= render partial: 'admins/user_actions/user_action_list', locals: { user_actions: @user_actions } %> +
    + + diff --git a/app/views/admins/user_actions/index.js.erb b/app/views/admins/user_actions/index.js.erb new file mode 100644 index 000000000..d624a61ae --- /dev/null +++ b/app/views/admins/user_actions/index.js.erb @@ -0,0 +1 @@ +$('.users-list-container').html("<%= j( render partial: 'admins/user_actions/user_action_list', locals: { user_actions: @user_actions } ) %>"); \ No newline at end of file diff --git a/app/views/api/v1/issues/_detail.json.jbuilder b/app/views/api/v1/issues/_detail.json.jbuilder index 8b5ff5949..941245b8c 100644 --- a/app/views/api/v1/issues/_detail.json.jbuilder +++ b/app/views/api/v1/issues/_detail.json.jbuilder @@ -27,18 +27,10 @@ json.milestone do end end json.author do - if issue.user.present? - json.partial! "api/v1/users/simple_user", locals: {user: issue.user} - else - json.nil! - end + json.partial! "api/v1/users/simple_user", locals: {user: issue.user} end json.changer do - if issue.changer.present? - json.partial! "api/v1/users/simple_user", locals: {user: issue.changer} - else - json.nil! - end + json.partial! "api/v1/users/simple_user", locals: {user: issue.changer} end json.assigners issue.show_assigners.each do |assigner| json.partial! "api/v1/users/simple_user", locals: {user: assigner} diff --git a/app/views/api/v1/issues/_simple_detail.json.jbuilder b/app/views/api/v1/issues/_simple_detail.json.jbuilder index 15259baa5..f3a4a12fd 100644 --- a/app/views/api/v1/issues/_simple_detail.json.jbuilder +++ b/app/views/api/v1/issues/_simple_detail.json.jbuilder @@ -33,11 +33,7 @@ json.time_scale issue.time_scale json.child_count issue.child_count json.author do - if issue.user.present? - json.partial! "api/v1/users/simple_user", locals: {user: issue.user} - else - json.nil! - end + json.partial! "api/v1/users/simple_user", locals: {user: issue.user} end json.assigners issue.show_assigners.each do |assigner| json.partial! "api/v1/users/simple_user", locals: {user: assigner} diff --git a/app/views/api/v1/issues/journals/_detail.json.jbuilder b/app/views/api/v1/issues/journals/_detail.json.jbuilder index 21040bc0a..e374dd04c 100644 --- a/app/views/api/v1/issues/journals/_detail.json.jbuilder +++ b/app/views/api/v1/issues/journals/_detail.json.jbuilder @@ -2,17 +2,13 @@ json.id journal.id json.is_journal_detail journal.is_journal_detail? json.created_at journal.created_on.strftime("%Y-%m-%d %H:%M") json.updated_at journal.updated_on.strftime("%Y-%m-%d %H:%M") -json.user do - if journal.user.present? - json.partial! "api/v1/users/simple_user", user: journal.user - else - json.nil! - end +json.user do + json.partial! "api/v1/users/simple_user", user: journal.user end if journal.is_journal_detail? detail = journal.journal_details.take - json.operate_category detail.property == "attr" ? detail.prop_key : detail.property - json.operate_content journal.is_journal_detail? ? journal.operate_content : nil + json.operate_category @issue.pm_issue_type.nil? ? detail.property == "attr" ? detail.prop_key : detail.property : journal.pm_operate_category + json.operate_content journal.is_journal_detail? ? @issue.pm_issue_type.nil? ? journal.operate_content : journal.pm_operate_content : nil else json.notes journal.notes json.comments_count journal.comments_count diff --git a/app/views/api/v1/users/_simple_user.json.jbuilder b/app/views/api/v1/users/_simple_user.json.jbuilder index 5e8e970e7..e1696ab93 100644 --- a/app/views/api/v1/users/_simple_user.json.jbuilder +++ b/app/views/api/v1/users/_simple_user.json.jbuilder @@ -4,6 +4,9 @@ if user.present? json.name user.real_name json.login user.login json.image_url Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(user).to_s -else - json.nil! +else + json.name "已注销" + json.login user&.login + json.type "User" + json.image_url Rails.application.config_for(:configuration)['platform_url'] + "/" + "images/account/del.svg" end \ No newline at end of file diff --git a/app/views/journals/_journal_item.json.jbuilder b/app/views/journals/_journal_item.json.jbuilder index 7522d915f..0d58892ab 100644 --- a/app/views/journals/_journal_item.json.jbuilder +++ b/app/views/journals/_journal_item.json.jbuilder @@ -1,7 +1,7 @@ json.id journal.id -json.user_name journal.user.try(:show_real_name) -json.user_login journal.user.try(:login) -json.user_picture url_to_avatar(journal.user) +json.user_name journal.user.blank?? "已注销": journal.user.show_real_name +json.user_login journal.user&.login +json.user_picture journal.user.blank?? "images/account/del.svg" : url_to_avatar(journal.user) json.is_journal_detail journal.is_journal_detail? #判断是否修改了参数而添加的回复内容 json.content journal.try(:notes) json.journal_details journal.journal_content diff --git a/app/views/project_trends/_detail.json.jbuilder b/app/views/project_trends/_detail.json.jbuilder index e7c590b8f..b9febc107 100644 --- a/app/views/project_trends/_detail.json.jbuilder +++ b/app/views/project_trends/_detail.json.jbuilder @@ -2,9 +2,9 @@ json.id trend.id json.trend_type trend.trend_type json.action_type l("trend.#{trend.action_type}") + l("trend.#{trend.trend_type}") json.trend_id trend.trend_id -json.user_name trend.user.try(:show_real_name) -json.user_login trend.user.login -json.user_avatar url_to_avatar(trend.user) +json.user_name trend.user.blank?? "已注销": trend.user.show_real_name +json.user_login trend.user&.login +json.user_avatar trend.user.blank?? "images/account/del.svg" : url_to_avatar(trend.user) json.action_time time_from_now(trend.created_at) json.project do json.owner do diff --git a/app/views/pull_requests/index.json.jbuilder b/app/views/pull_requests/index.json.jbuilder index 3a35772f0..9f371cb21 100644 --- a/app/views/pull_requests/index.json.jbuilder +++ b/app/views/pull_requests/index.json.jbuilder @@ -35,9 +35,9 @@ json.issues do json.pr_full_time pr.status == 1 ? pr.updated_at : issue.updated_on json.assign_user_name issue.get_assign_user.try(:show_real_name) json.assign_user_login issue.get_assign_user.try(:login) - json.author_name issue.user.try(:show_real_name) + json.author_name issue.user.blank?? "已注销": issue.user.show_real_name json.author_login issue.user.try(:login) - json.avatar_url url_to_avatar(issue.user) + json.avatar_url issue.user.blank?? "images/account/del.svg" : url_to_avatar(issue.user) json.priority issue.priority.try(:name) json.version issue.version.try(:name) json.journals_count issue.get_journals_size diff --git a/app/views/pull_requests/show.json.jbuilder b/app/views/pull_requests/show.json.jbuilder index 509577edc..304457f82 100644 --- a/app/views/pull_requests/show.json.jbuilder +++ b/app/views/pull_requests/show.json.jbuilder @@ -44,9 +44,9 @@ json.issue do json.created_at format_time(@issue.created_on) json.assign_user_name @issue_assign_to.try(:show_real_name) json.assign_user_login @issue_assign_to.try(:login) - json.author_name @issue_user.try(:show_real_name) + json.author_name @issue_user.blank?? "已注销": @issue_user.show_real_name json.author_login @issue_user.try(:login) - json.author_picture url_to_avatar(@issue_user) + json.author_picture @issue_user.blank?? "images/account/del.svg" : url_to_avatar(@issue_user) json.issue_status @issue.issue_status.try(:name) json.priority @issue.priority.try(:name) json.version @issue.version.try(:name) diff --git a/app/views/users/_user_simple.json.jbuilder b/app/views/users/_user_simple.json.jbuilder index 5da161509..30343d75e 100644 --- a/app/views/users/_user_simple.json.jbuilder +++ b/app/views/users/_user_simple.json.jbuilder @@ -4,6 +4,9 @@ if user.present? json.name user.real_name json.login user.login json.image_url url_to_avatar(user) -else - json.nil! +else + json.name "已注销" + json.login user&.login + json.type "User" + json.image_url Rails.application.config_for(:configuration)['platform_url'] + "/" + "images/account/del.svg" end \ No newline at end of file diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index 901ea87f0..20b45b1c4 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -4,7 +4,7 @@ json.nickname @user.nickname json.gender @user.gender json.login @user.login json.user_id @user.id -json.image_url url_to_avatar(@user) +json.image_url url_to_avatar(@user).blank? ? User::Avatar.get_letter_avatar_url("@") : url_to_avatar(@user) json.admin @user.admin? json.is_teacher @user.user_extension&.teacher? json.user_identity @user.identity diff --git a/app/views/version_releases/_version_release.json.jbuilder b/app/views/version_releases/_version_release.json.jbuilder index 9835db152..83bfd8e1f 100644 --- a/app/views/version_releases/_version_release.json.jbuilder +++ b/app/views/version_releases/_version_release.json.jbuilder @@ -11,9 +11,9 @@ json.zipball_url render_zip_url(@owner, @repository, version&.tag_name) json.draft version&.draft ? "草稿" : (version&.prerelease ? "预发行" : "稳定") json.created_at format_time(version.created_at.to_s.to_time) json.published_at format_time(version.created_at.to_s.to_time) -json.user_name user.present? ? user.try(:show_real_name) : "" +json.user_name user.present? ? user.try(:show_real_name) : "已注销" json.user_login user&.login -json.image_url user.present? ? url_to_avatar(user) : "" +json.image_url user.present? ? url_to_avatar(user) : "images/account/del.svg" json.attachments do json.array! version.try(:attachments) do |attachment| # json.partial! "attachments/attachment_simple", locals: {attachment: attachment} diff --git a/config/routes.rb b/config/routes.rb index 64d064b86..501a024b4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -92,7 +92,7 @@ Rails.application.routes.draw do resources :project_rank, only: [:index] resources :user_rank, only: [:index] resources :nps, only: [:create] - + resources :statistic, only: [:index] do collection do get :platform_profile @@ -1025,6 +1025,7 @@ Rails.application.routes.draw do post :drag, on: :collection post :replace_image_url, on: :member end + resources :user_actions , only:[:index] resources :faqs resources :nps do post :switch_change, on: :collection diff --git a/config/routes/api.rb b/config/routes/api.rb index 945a1dc29..91900545b 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -58,6 +58,7 @@ defaults format: :json do post :check_email post :check_email_verify_code post :check_phone_verify_code + post :check_user_can_delete patch :update_email patch :update_phone end diff --git a/db/migrate/202410926031029_add_user_info_to_user_actions.rb b/db/migrate/202410926031029_add_user_info_to_user_actions.rb new file mode 100644 index 000000000..967e8a68d --- /dev/null +++ b/db/migrate/202410926031029_add_user_info_to_user_actions.rb @@ -0,0 +1,20 @@ +class AddUserInfoToUserActions < ActiveRecord::Migration[5.2] + def change + add_column :user_actions, :login, :string + add_column :user_actions, :phone, :string + add_column :user_actions, :email, :string + add_column :user_actions, :memo, :text + + UserAction.find_in_batches(batch_size: 1000) do |sw| + Parallel.each(sw, in_threads: 5) do |user_action| + if user_action.user.present? + user_action.login = user_action.user&.login + user_action.email = user_action.user&.mail + user_action.phone = user_action.user&.phone + user_action.save + end + end + end + end + +end diff --git a/lib/tasks/batch_user_add_projects.rake b/lib/tasks/batch_user_add_projects.rake new file mode 100644 index 000000000..c2158117b --- /dev/null +++ b/lib/tasks/batch_user_add_projects.rake @@ -0,0 +1,25 @@ +namespace :batch_user_add_projects do + desc "batch add projects" + task done: :environment do + user_id = ENV['user_id'] + puts "user_id=================#{user_id}" + next if user_id.blank? + project_ids = Project.find_by_sql("SELECT id FROM projects WHERE forked_from_project_id IN (SELECT id FROM projects WHERE user_id IN (SELECT id FROM users WHERE login='OSKYCX'))").pluck(:id) + if ENV['project_id'].present? + project_ids = [ENV['project_id']] + end + next if project_ids.blank? + puts "project_ids=================#{project_ids}" + project_ids.each do |project_id| + begin + user = User.find_by(id: user_id) + next if user.blank? + project = Project.find_by(id: project_id) + next if project.blank? + Projects::AddMemberInteractor.call(project.owner, project, user, "admin") + rescue Exception => e + puts "batch add projects error: #{user_id}" + end + end + end +end \ No newline at end of file diff --git a/public/images/account/del.svg b/public/images/account/del.svg new file mode 100644 index 000000000..2c3420924 --- /dev/null +++ b/public/images/account/del.svg @@ -0,0 +1,6 @@ + + + + + +