Merge branch 'pre_trustie_server' into trustie_server

This commit is contained in:
xxq250 2024-10-15 08:59:31 +08:00
commit 13517c8eab
45 changed files with 384 additions and 80 deletions

View File

@ -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])

View File

@ -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)

View File

@ -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

View File

@ -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)
# 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
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)}")

View File

@ -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

View File

@ -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

View File

@ -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
#
@ -20,4 +24,75 @@
#
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}<br/>邮箱:#{user&.mail}<br/>手机号:#{user&.phone}<br/>昵称:#{user&.nickname}<br/>"
when "DestroyProject" then "项目名称:#{project&.name}<br/>项目标识:#{project&.identifier}<br/>"
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

View File

@ -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)

View File

@ -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)

View File

@ -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!)

View File

@ -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!)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -17,7 +17,7 @@
<td>
<%= edu_setting.name %>
</td>
<td><%= edu_setting.value %></td>
<td style="word-break: break-all;"><%= edu_setting.value %></td>
<td><%= overflow_hidden_span display_text(edu_setting.description), width: 200 %></td>
<td><%= edu_setting.created_at&.strftime('%Y-%m-%d %H:%M') %></td>

View File

@ -4,7 +4,7 @@
<div class="box search-form-container project-list-form">
<%= 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': '搜索中...') %>
<input type="reset" class="btn btn-secondary clear-btn" value="清空"/>
<div class="form-group mr-2 pull-right" style='margin-left:auto'>

View File

@ -26,6 +26,7 @@
<li><%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs', has_permission: current_user.admin? || current_user.business?) %></li>
<li><%= sidebar_item(admins_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps', has_permission: current_user.admin? || current_user.business?) %></li>
<li><%= sidebar_item(admins_feedbacks_path, '用户反馈', icon: 'question-circle', controller: 'admins-feedbacks', has_permission: current_user.admin? || current_user.business?) %></li>
<li><%= sidebar_item(admins_user_actions_path, '操作记录', icon: 'pencil-square', controller: 'admins-user_actions', has_permission: current_user.admin?) %></li>
<li><%= sidebar_item(admins_system_notifications_path, '系统公告配置', icon: 'bell', controller: 'admins-system_notifications', has_permission: current_user.admin? || current_user.business?) %></li>
<% end %>
</li>

View File

@ -0,0 +1,35 @@
<table class="table table-hover users-list-table">
<thead class="thead-light">
<tr>
<th width="5%" class="text-center">序号</th>
<th width="10%">操作类型</th>
<th width="10%" class="text-left">操作人账号</th>
<th width="25%" class="text-left">关联基本信息</th>
<th width="15%">操作时间</th>
<th width="35%">原因/备注</th>
</tr>
</thead>
<tbody>
<% if user_actions.present? %>
<% user_actions.each_with_index do |action, index| %>
<tr class="user-item-<%= action.user&.id %>">
<td class="text-center"><%= list_index_no((params[:page] || 1).to_i, index) %></td>
<td><%= action.action_name %></td>
<td>
<%= link_to "/#{action.user&.login}", target: '_blank' do %>
<%= overflow_hidden_span action.opt_user_name, width: 100 %>
<% end %></td>
<td>
<%= raw action.action_info %>
</td>
<td><%= display_text(action.created_at&.strftime('%Y-%m-%d %H:%M')) %></td>
<td><%= action.memo %></td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: user_actions } %>

View File

@ -0,0 +1,33 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('操作记录', admins_user_actions_path) %>
<% end %>
<div class="box search-form-container user-list-form">
<%= 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 %>
</div>
<div class="box admin-list-container users-list-container">
<%= render partial: 'admins/user_actions/user_action_list', locals: { user_actions: @user_actions } %>
</div>
<script>
$(function () {
$('#action_type_select').change(function () {
var switch_value = $(this).val();
$('#action_type').val(switch_value);
if (switch_value == ''){
$('#action_type').show();
}else{
$('#action_type').hide();
}
})
})
</script>

View File

@ -0,0 +1 @@
$('.users-list-container').html("<%= j( render partial: 'admins/user_actions/user_action_list', locals: { user_actions: @user_actions } ) %>");

View File

@ -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}

View File

@ -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}

View File

@ -3,16 +3,12 @@ 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.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

View File

@ -5,5 +5,8 @@ if user.present?
json.login user.login
json.image_url Rails.application.config_for(:configuration)['platform_url'] + "/" + url_to_avatar(user).to_s
else
json.nil!
json.name "已注销"
json.login user&.login
json.type "User"
json.image_url Rails.application.config_for(:configuration)['platform_url'] + "/" + "images/account/del.svg"
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -5,5 +5,8 @@ if user.present?
json.login user.login
json.image_url url_to_avatar(user)
else
json.nil!
json.name "已注销"
json.login user&.login
json.type "User"
json.image_url Rails.application.config_for(:configuration)['platform_url'] + "/" + "images/account/del.svg"
end

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="70" height="70" viewBox="0 0 70 70">
<g id="组_9971" data-name="组 9971" transform="translate(21840 14407)">
<circle id="椭圆_227" data-name="椭圆 227" cx="35" cy="35" r="35" transform="translate(-21840 -14407)" fill="#8f959e"/>
<path id="已注销" d="M101.137,69.2a9.2,9.2,0,1,1-9.2,9.2,9.2,9.2,0,0,1,9.2-9.2Zm-9.619-2.768a14.323,14.323,0,0,1,5.108.936,11.5,11.5,0,0,0-5.069,18.551l-16.645,0a5.127,5.127,0,0,1-5.127-5.127A14.357,14.357,0,0,1,83.614,66.446l.528-.012h7.375Zm13.068,10.817h-6.9a1.15,1.15,0,0,0-.207,2.281l.207.018h6.9a1.15,1.15,0,0,0,0-2.3ZM87.83,45.978a9.815,9.815,0,1,1-9.815,9.815A9.815,9.815,0,0,1,87.83,45.978Z" transform="translate(-21894.785 -14438.978)" fill="#fff" opacity="0.94"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 784 B