* 改版升级个人主页
* 修复bug
* 更改license
This commit is contained in:
jasder 2021-06-09 09:52:57 +08:00
commit c5d73b7cf8
104 changed files with 4153 additions and 525 deletions

View File

@ -176,7 +176,9 @@ GEM
mimemagic (~> 0.3.2)
maruku (0.7.3)
method_source (0.9.2)
mimemagic (0.3.4)
mimemagic (0.3.10)
nokogiri (~> 1)
rake
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.0)

137
LICENSE
View File

@ -1,21 +1,124 @@
MIT License
木兰宽松许可证, 第2版
Copyright (c) [year] [fullname]
2020年1月 http://license.coscl.org.cn/MulanPSL2
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
您对“软件”的复制、使用、修改及分发受木兰宽松许可证第2版“本许可证”的如下条款的约束
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
0. 定义
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体” 是指提交贡献的机构及其“关联实体”。
“关联实体” 是指对“本许可证”下的行为方而言控制、受控制或与其共同受控制的机构此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
条款结束
如何将木兰宽松许可证第2版应用到您的软件
如果您希望将木兰宽松许可证第2版应用到您的新软件为了方便接收者查阅建议您完成如下三步
1 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2 请您在软件包的一级目录下创建以“LICENSE”为名的文件将整个许可证文本放入该文件中
3 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
Mulan Permissive Software LicenseVersion 2
Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
0. Definition
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its Affiliates.
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, control means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW ITS CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
Attach the statement to the appropriate annotated syntax at the beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.

View File

@ -196,6 +196,25 @@ class AccountsController < ApplicationController
# session[:user_id] = @user.id
end
def change_password
@user = User.find_by(login: params[:login])
return render_error("未找到相关用户!") if @user.blank?
return render_error("旧密码不正确") unless @user.check_password?(params[:old_password])
sync_params = {
password: params[:password].to_s,
email: @user.mail
}
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
if interactor.success?
@user.update_attribute(:password, params[:password])
render_ok
else
render_error(interactor.error)
end
end
# 忘记密码
def reset_password
begin

View File

@ -32,7 +32,7 @@ class Admins::AuthSchoolsController < Admins::BaseController
def search_manager
school = School.find_by(id: params[:school_id])
user_ids = school&.ec_school_users&.pluck(:user_id)
@users = User.where.not(id: user_ids).where("concat(lastname, firstname) like ?", "%#{params[:name].strip.to_s}%").limit(10)
@users = User.where.not(id: user_ids).where("CONCAT_WS(lastname, firstname, nickname) like ?", "%#{params[:name].strip.to_s}%").limit(10)
end
# 添加认证学校管理员

View File

@ -32,7 +32,7 @@ class Admins::LaboratoriesController < Admins::BaseController
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword '\
like_sql = 'shixuns.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword '\
'OR mirror_repositories.name LIKE :keyword'
shixuns = shixuns.joins(:user, :mirror_repositories).where(like_sql, keyword: "%#{keyword}%")
end
@ -48,7 +48,7 @@ class Admins::LaboratoriesController < Admins::BaseController
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
like_sql = 'subjects.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword'
subjects = subjects.joins(:user).where(like_sql, keyword: "%#{keyword}%")
end

View File

@ -773,7 +773,26 @@ class ApplicationController < ActionController::Base
def base_url
request.base_url
end
def convert_image!
@image = params[:image] || user_params[:image]
return unless @image.present?
max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
if @image.class == ActionDispatch::Http::UploadedFile
render_error('请上传文件') if @image.size.zero?
render_error('文件大小超过限制') if @image.size > max_size.to_i
else
image = @image.to_s.strip
return render_error('请上传正确的图片') if image.blank?
@image = Util.convert_base64_image(image, max_size: max_size.to_i)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
def avatar_path(object)
ApplicationController.helpers.disk_filename(object.class, object.id)
end
private
def object_not_found

View File

@ -18,7 +18,7 @@ class MembersController < ApplicationController
scope = @project.members.includes(:roles, user: :user_extension)
search = params[:search].to_s.downcase
role = params[:role].to_s
scope = scope.joins(:user).where("LOWER(concat(users.lastname, users.firstname, users.login, users.mail)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
scope = scope.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
scope = scope.joins(:roles).where("roles.name LIKE ?", "%#{role}%") if role.present?
@total_count = scope.size

View File

@ -5,7 +5,7 @@ class Organizations::OrganizationUsersController < Organizations::BaseController
def index
@organization_users = @organization.organization_users.includes(:user)
search = params[:search].to_s.downcase
@organization_users = @organization_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
@organization_users = @organization_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
@organization_users = kaminari_paginate(@organization_users)
end

View File

@ -70,25 +70,6 @@ class Organizations::OrganizationsController < Organizations::BaseController
end
private
def convert_image!
return unless params[:image].present?
max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
if params[:image].class == ActionDispatch::Http::UploadedFile
@image = params[:image]
render_error('请上传文件') if @image.size.zero?
render_error('文件大小超过限制') if @image.size > max_size.to_i
else
image = params[:image].to_s.strip
return render_error('请上传正确的图片') if image.blank?
@image = Util.convert_base64_image(image, max_size: max_size.to_i)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
def avatar_path(organization)
ApplicationController.helpers.disk_filename(organization.class, organization.id)
end
def organization_params
params.permit(:name, :description, :website, :location,

View File

@ -177,7 +177,7 @@ class ProjectsController < ApplicationController
end
def recommend
@projects = Project.recommend.includes(:repository, :project_category, :owner).order(id: :desc)
@projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc)
end
def about

View File

@ -0,0 +1,26 @@
class Users::HeadmapsController < Users::BaseController
def index
result = Gitea::User::HeadmapService.call(observed_user.login, start_stamp, end_stamp)
@headmaps = result[2]
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def start_stamp
if params[:year].present?
Date.new(params[:year], 1).to_time.to_i
else
Date.today.to_time.to_i - 365*24*60*60
end
end
def end_stamp
if params[:year].present?
Date.new(params[:year], 1).to_time.to_i + 365*24*60*60
else
Date.today.to_time.to_i
end
end
end

View File

@ -0,0 +1,45 @@
class Users::IsPinnedProjectsController < Users::BaseController
before_action :private_user_resources!, only: [:pin]
def index
@is_pinned_projects = observed_user.pinned_projects.order(position: :desc, created_at: :asc).includes(project: [:project_category, :project_language, :repository]).order(position: :desc)
@is_pinned_projects = kaminari_paginate(@is_pinned_projects)
end
def pin
observed_user.is_pinned_project_ids = is_pinned_project_ids
render_ok
rescue ActiveRecord::RecordNotFound => e
render_not_found
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def update
@pinned_project = PinnedProject.find_by_id(params[:id])
@pinned_project.attributes = pinned_project_params
if @pinned_project.save
render_ok
else
render_error
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
private
def is_pinned_project_ids
if params[:is_pinned_project_ids].present?
return params[:is_pinned_project_ids].select{|id| observed_user.full_member_projects.visible.pluck(:id).include?(id.to_i) }
end
if params[:is_pinned_project_id].present?
return observed_user.is_pinned_project_ids unless observed_user.full_member_projects.visible.pluck(:id).include?(params[:is_pinned_project_id].to_i)
return observed_user.is_pinned_project_ids.include?(params[:is_pinned_project_id].to_i) ? observed_user.is_pinned_project_ids : observed_user.is_pinned_project_ids.push(params[:is_pinned_project_id].to_i)
end
end
def pinned_project_params
params.require(:pinned_project).permit(:position)
end
end

View File

@ -0,0 +1,11 @@
class Users::ProjectTrendsController < Users::BaseController
def index
if params[:date].present?
@project_trends = observed_user.project_trends.where("DATE(created_at) = ?", params[:date])
else
@project_trends = observed_user.project_trends
end
@project_trends = kaminari_paginate(@project_trends.includes(:trend, :project).order(created_at: :desc))
end
end

View File

@ -0,0 +1,217 @@
class Users::StatisticsController < Users::BaseController
before_action :preload_develop_data, only: [:develop]
# 近期活动统计
def activity
date_range = (1.week.ago.to_date..Date.today).to_a
commit_request = Gitea::User::HeadmapService.call(observed_user.login, 1.week.ago.to_date.to_time.to_i, Date.today.to_time.to_i)
commit_data = commit_request[2]
@date_data = []
@issue_data = []
@pull_request_data = []
@commit_data = []
date_range.each do |date|
@date_data << date.strftime("%Y.%m.%d")
@issue_data << observed_user.issues.where("DATE(created_on) = ?", date).size
@pull_request_data << observed_user.pull_requests.where("DATE(created_at) = ?", date).size
date_commit_data = commit_data.select{|item| item["timestamp"] == date.to_time.to_i}
@commit_data << (date_commit_data.blank? ? 0 : date_commit_data[0]["contributions"].to_i)
end
render :json => {dates: @date_data, issues_count: @issue_data, pull_requests_count: @pull_request_data, commits_count: @commit_data}
end
# 开发能力
def develop
if params[:start_time].present? && params[:end_time].present?
# 影响力
@influence = (60.0 + @follow_count / (@follow_count + 10.0) * 40.0).to_i
@platform_influence = (60.0 + @platform_follow_count / (@platform_follow_count + 10.0) * 40.0).to_i
# 贡献度
@contribution = (60.0 + @pullrequest_count / (@pullrequest_count + 10.0) * 40.0).to_i
@platform_contribution = (60.0 + @platform_pullrequest_count / (@platform_pullrequest_count + 10.0) * 40.0).to_i
# 活跃度
@activity = (60.0 + @issues_count / (@issues_count + 10.0) * 40.0).to_i
@platform_activity = (60.0 + @platform_issues_count / (@platform_issues_count + 10.0) * 40.0).to_i
# 项目经验
@experience = 10 * @project_count + 5 * @fork_count + @project_watchers_count + @project_praises_count
@experience = (60.0 + @experience / (@experience + 100.0) * 40.0).to_i
@platform_experience = 10 * @platform_project_count + 5 * @platform_fork_count + @platform_project_watchers_count + @platform_project_praises_count
@platform_experience = (60.0 + @platform_experience / (@platform_experience + 100.0) * 40.0).to_i
# 语言能力
@language = (60.0 + @project_languages_count.length / (@project_languages_count.length + 5.0) * 40.0).to_i
@platform_language = (60.0 + @platform_project_languages_count.length / (@platform_project_languages_count.length + 5.0) * 40.0).to_i
# 语言百分比
@languages_percent = Hash.new
for key in @project_languages_count.keys do
@languages_percent[key] = (@project_languages_count[key].to_f / @project_languages_count.values.sum).round(2)
end
@platform_languages_percent = Hash.new
for key in @platform_project_languages_count.keys do
@platform_languages_percent[key] = (@platform_project_languages_count[key].to_f / @platform_project_languages_count.values.sum).round(2)
end
# 各门语言分数
@each_language_score = Hash.new
for key in @project_languages_count.keys do
@each_language_score[key] = (60.0 + @project_languages_count[key] / (@project_languages_count[key] + 5.0) * 40.0).to_i
end
@platform_each_language_score = Hash.new
for key in @platform_project_languages_count.keys do
@platform_each_language_score[key] = (60.0 + @platform_project_languages_count[key] / (@platform_project_languages_count[key] + 5.0) * 40.0).to_i
end
else
# 影响力
@influence = (60.0 + @follow_count / (@follow_count + 20.0) * 40.0).to_i
@platform_influence = (60.0 + @platform_follow_count / (@platform_follow_count + 20.0) * 40.0).to_i
# 贡献度
@contribution = (60.0 + @pullrequest_count / (@pullrequest_count + 20.0) * 40.0).to_i
@platform_contribution = (60.0 + @platform_pullrequest_count / (@platform_pullrequest_count + 20.0) * 40.0).to_i
# 活跃度
@activity = (60.0 + @issues_count / (@issues_count + 80.0) * 40.0).to_i
@platform_activity = (60.0 + @platform_issues_count / (@platform_issues_count + 80.0) * 40.0).to_i
# 项目经验
@experience = 10 * @project_count + 5 * @fork_count + @project_watchers_count + @project_praises_count
@experience = (60.0 + @experience / (@experience + 100.0) * 40.0).to_i
@platform_experience = 10 * @platform_project_count + 5 * @platform_fork_count + @platform_project_watchers_count + @platform_project_praises_count
@platform_experience = (60.0 + @platform_experience / (@platform_experience + 100.0) * 40.0).to_i
# 语言能力
@language = (60.0 + @project_languages_count.length / (@project_languages_count.length + 5.0) * 40.0).to_i
@platform_language = (60.0 + @platform_project_languages_count.length / (@platform_project_languages_count.length + 5.0) * 40.0).to_i
# 语言百分比
@languages_percent = Hash.new
for key in @project_languages_count.keys do
@languages_percent[key] = (@project_languages_count[key].to_f / @project_languages_count.values.sum).round(2)
end
# @platform_languages_percent = Hash.new
# for key in @platform_project_languages_count.keys do
# @platform_languages_percent[key] = (@platform_project_languages_count[key].to_f / @platform_project_languages_count.values.sum).round(2)
# end
# 各门语言分数
@each_language_score = Hash.new
for key in @project_languages_count.keys do
@each_language_score[key] = (60.0 + @project_languages_count[key] / (@project_languages_count[key] + 5.0) * 40.0).to_i
end
# @platform_each_language_score = Hash.new
# for key in @platform_project_languages_count.keys do
# @platform_each_language_score[key] = (60.0 + @platform_project_languages_count[key] / (@platform_project_languages_count[key] + 5.0) * 40.0).to_i
# end
end
end
# 角色定位
def role
full_member_projects = observed_user.full_member_projects
owner_projects = filter_member_projects_by_role("Owner")
manager_projects = filter_member_projects_by_role("Manager").where.not(id: owner_projects.ids)
developer_projects = filter_member_projects_by_role("Developer").where.not(id: owner_projects.ids + manager_projects.ids)
reporter_projects = filter_member_projects_by_role("Reporter").where.not(id: owner_projects.ids + manager_projects.ids + developer_projects.ids)
@full_member_projects_count = full_member_projects.size
@owner_projects_count = owner_projects.size
@manager_projects_count = manager_projects.size
@developer_projects_count = developer_projects.size
@reporter_projects_count = reporter_projects.size
end
# 专业定位
def major
# 参与项目
join_normal_projects_sql = Project.members_projects(observed_user.id).to_sql
join_org_projects_sql = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observed_user.id}).to_sql
# 关注项目
star_projects_sql = Project.joins(:watchers).where(watchers: {watchable_type: "Project", user_id: observed_user.id}).to_sql
# fork项目
fork_projects_sql = Project.where(id: observed_user.fork_users.select(:id, :fork_project_id).pluck(:fork_project_id)).to_sql
major_projects = Project.from("( #{ join_normal_projects_sql} UNION #{ join_org_projects_sql } UNION #{ star_projects_sql } UNION #{fork_projects_sql}) AS projects").distinct
categories = ProjectCategory.joins(:projects).merge(Project.where(id: time_filter(major_projects, 'created_on'))).distinct.pluck(:name)
render :json => {categories: categories}
end
private
def time_filter(collection, time_field)
if params[:start_time].present? && params[:end_time].present?
return collection.where("#{time_field} > ? AND #{time_field} < ?", Time.at(params[:start_time].to_i).beginning_of_day, Time.at(params[:end_time].to_i).end_of_day)
else
return collection
end
rescue
return collection
end
def filter_member_projects_by_role(role)
case role
when 'Owner'
normal_projects_sql = Project.joins(members: :roles).where(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Manager'}).to_sql
org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "owner"}, team_users: {user_id: observed_user.id}).to_sql
when "Manager"
normal_projects_sql = Project.joins(members: :roles).where.not(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Manager'}).to_sql
org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "admin"}, team_users: {user_id: observed_user.id}).to_sql
when "Developer"
normal_projects_sql = Project.joins(members: :roles).where.not(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Developer'}).to_sql
org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "write"}, team_users: {user_id: observed_user.id}).to_sql
when "Reporter"
normal_projects_sql = Project.joins(members: :roles).where.not(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Reporter'}).to_sql
org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "read"}, team_users: {user_id: observed_user.id}).to_sql
end
return time_filter(Project.from("( #{normal_projects_sql} UNION #{org_projects_sql} ) AS projects").distinct, 'created_on')
end
def preload_develop_data
if params[:start_time].present? && params[:end_time].present?
# 用户被follow数量
@follow_count = time_filter(Watcher.where(watchable: observed_user), 'created_at').count
@platform_follow_count = time_filter(Watcher.where(watchable_type: 'User'), 'created_at').count
# 用户pr数量
@pullrequest_count = time_filter(PullRequest.where(user_id: observed_user.id), 'created_at').count
@platform_pullrequest_count = time_filter(PullRequest, 'created_at').count
# 用户issue数量
@issues_count = time_filter(Issue.where(author_id: observed_user.id), 'created_on').count
@platform_issues_count = time_filter(Issue, 'created_on').count
# 用户总项目数
@project_count = time_filter(Project.where(user_id:observed_user.id), 'created_on').count
@platform_project_count = time_filter(Project, 'created_on').count
# 用户项目被fork数量
@fork_count = time_filter(Project.where(user_id:observed_user.id), 'created_on').sum("forked_count")
@platform_fork_count = time_filter(Project, 'created_on').sum("forked_count")
# 用户项目关注数
@project_watchers_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').sum("watchers_count")
@platform_project_watchers_count = time_filter(Project, 'created_on').sum("watchers_count")
# 用户项目点赞数
@project_praises_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').sum("praises_count")
@platform_project_praises_count = time_filter(Project, 'created_on').sum("praises_count")
# 用户不同语言项目数量
@project_languages_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').joins(:project_language).group("project_languages.name").count
@platform_project_languages_count = time_filter(Project, 'created_on').joins(:project_language).group("project_languages.name").count
else
# 用户被follow数量
@follow_count = Cache::UserFollowCountService.call(observed_user)
@platform_follow_count = Cache::PlatformFollowCountService.call
# 用户pr数量
@pullrequest_count = Cache::UserPullrequestCountService.call(observed_user)
@platform_pullrequest_count = Cache::PlatformPullrequestCountService.call
# 用户issue数量
@issues_count = Cache::UserIssueCountService.call(observed_user)
@platform_issues_count = Cache::PlatformIssueCountService.call
# 用户总项目数
@project_count = Cache::UserProjectCountService.call(observed_user)
@platform_project_count = Cache::PlatformProjectCountService.call
# 用户项目被fork数量
@fork_count = Cache::UserProjectForkCountService.call(observed_user)
@platform_fork_count = Cache::PlatformProjectForkCountService.call
# 用户项目关注数
@project_watchers_count = Cache::UserProjectWatchersCountService.call(observed_user)
@platform_project_watchers_count = Cache::PlatformProjectWatchersCountService.call
# 用户项目点赞数
@project_praises_count = Cache::UserProjectPraisesCountService.call(observed_user)
@platform_project_praises_count = Cache::PlatformProjectPraisesCountService.call
# 用户不同语言项目数量
@project_languages_count = Cache::UserProjectLanguagesCountService.call(observed_user)
@platform_project_languages_count = Cache::PlatformProjectLanguagesCountService.call
end
end
end

View File

@ -6,6 +6,7 @@ class UsersController < ApplicationController
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
before_action :require_login, only: %i[me list sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
before_action :convert_image!, only: [:update]
skip_before_action :check_sign, only: [:attachment_show]
def connect_to_ci_db(options={})
@ -62,7 +63,7 @@ class UsersController < ApplicationController
def fan_users
watchers = @user.watchers.includes(:user).order("watchers.created_at desc")
watchers = watchers.joins(:user).where("LOWER(concat(users.lastname, users.firstname, users.login)) LIKE ?", "%#{params[:search].split(" ").join('|')}%") if params[:search].present?
watchers = watchers.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.nickname)) LIKE ?", "%#{params[:search].split(" ").join('|')}%") if params[:search].present?
@watchers_count = watchers.size
@watchers = paginate(watchers)
@ -72,9 +73,13 @@ class UsersController < ApplicationController
end
def update
@user = User.find params[:id]
@user.update!(user_params)
render_ok
return render_not_found unless @user = User.find_by_id(params[:id]) || User.find_by(login: params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
Util.write_file(@image, avatar_path(@user)) if user_params[:image].present?
@user.attributes = user_params.except(:image)
unless @user.save
render_error(@user.errors.full_messages.join(", "))
end
end
def me
@ -274,11 +279,13 @@ class UsersController < ApplicationController
end
def user_params
params.require(:user).permit(:nickname, :lastname, :show_realname,:login,:mail,
params.require(:user).permit(:nickname, :image,
user_extension_attributes: [
:gender, :location, :location_city,
:occupation, :technical_title,
:school_id, :department_id,:identity, :student_id, :description]
:school_id, :department_id, :province, :city,
:custom_department, :identity, :student_id, :description,
:show_email, :show_location, :show_department]
)
end

View File

@ -11,7 +11,7 @@ class UsersForPrivateMessagesController < ApplicationController
return
end
users = users.where('LOWER(concat(lastname, firstname, nickname)) LIKE ?', "%#{keyword}%")
users = users.where('LOWER(CONCAT_WS(lastname, firstname, nickname)) LIKE ?', "%#{keyword}%")
@users = users.limit(10).includes(:user_extension)
end

View File

@ -86,7 +86,7 @@ class Weapps::CoursesController < Weapps::BaseController
end
if search.present?
@teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT(users.lastname, users.firstname)) like ?", "%#{search}%")
@teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) like ?", "%#{search}%")
end
@teacher_list_size = @teacher_list.size
@ -127,7 +127,7 @@ class Weapps::CoursesController < Weapps::BaseController
@students = CourseMember.students(@course)
if search.present?
@students = @students.joins(user: :user_extension).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? or
@students = @students.joins(user: :user_extension).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) like ? or
user_extensions.student_id like ?", "%#{search}%", "%#{search}%")
end

View File

@ -86,7 +86,7 @@ class ZipsController < ApplicationController
#搜索
if params[:search].present?
@ex_users = @ex_users.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%")
@ex_users = @ex_users.joins(user: :user_extension).where("CONCAT_WS(lastname, firstname, nickname) like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%")
end
default_ex_users_size = @ex_users&.size
@ -130,7 +130,7 @@ class ZipsController < ApplicationController
end
unless params[:search].blank?
@all_student_works = @all_student_works.joins(user: :user_extension).where("concat(lastname, firstname) like ?
@all_student_works = @all_student_works.joins(user: :user_extension).where("CONCAT_WS(lastname, firstname, nickname) like ?
or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%")
end

View File

@ -1,7 +1,7 @@
<!--
* @Date: 2021-03-01 10:35:21
* @LastEditors: viletyy
* @LastEditTime: 2021-04-26 10:47:30
* @LastEditTime: 2021-06-03 10:18:53
* @FilePath: /forgeplus/app/docs/slate/source/includes/_users.md
-->
# Users
@ -47,6 +47,918 @@ await octokit.request('GET /api/users/me.json')
Success Data.
</aside>
## 更改用户信息
更改用户信息
> 示例:
```shell
curl -X PATCH/PUT http://localhost:3000/api/users/yystopf.json
```
```javascript
await octokit.request('PATCH/PUT /api/users/:login.json')
```
### HTTP 请求
`PATCH/PUT /api/users/:login.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|user.nickname |string |用户昵称 |
|user.image |base64/file |用户头像 |
|user.user_extension_attributes.gender |int |性别, 0男 1女 |
|user.user_extension_attributes.province |string |省份 |
|user.user_extension_attributes.city |string |城市 |
|user.user_extension_attributes.description |string |简介 |
|user.user_extension_attributes.custom_department|string |单位名称 |
|user.user_extension_attributes.technical_title |string |职业 |
|user.user_extension_attributes.show_email |bool |是否展示邮箱 |
|user.user_extension_attributes.show_location |bool |是否展示位置 |
|user.user_extension_attributes.show_department |bool |是否展示公司 |
> 请求的JSON示例
```json
{
"user": {
"nickname": "xxx",
"user_extension_attributes": {
"gender": 0,
"province": "湖南",
"city": "长沙",
"description": "个性签名",
"custom_department": "湖南智擎科技有限公司",
}
}
}
```
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 获取用户星标项目
获取用户星标项目
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/is_pinned_projects.json
```
```javascript
await octokit.request('GET /api/users/:login/is_pinned_projects.json')
```
### HTTP 请求
`GET api/users/:login/is_pinned_projects.json`
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_count |int |星标项目数量 |
|identifier |string |项目标识 |
|name |string |项目名称 |
|description |string |项目描述 |
|visits |int |项目访问数量|
|praises_count |int |项目点赞数量|
|watchers_count |int |项目关注数量|
|issues_count |int |项目issue数量|
|pull_requests_count |int |项目合并请求数量|
|forked_count |int |项目复刻数量|
|is_public |bool |项目是否公开|
|mirror_url |string |镜像地址|
|type |int |项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目|
|time_ago |string |上次更新时间|
|open_devops |int |是否开启devops|
|forked_from_project_id |int |fork项目id|
|platform |string |项目平台|
|author.name |string |项目拥有者名称|
|author.type |string |项目拥有者类型|
|author.login |string |项目拥有者用户名|
|author.image_url |string |项目拥有者头像|
|category.name |string |项目分类名称|
|language.name |string |项目语言名称|
|position |int |项目排序|
> 返回的JSON示例:
```json
{
"total_count": 1,
"projects": [
{
"id": 89,
"repo_id": 89,
"identifier": "monkey",
"name": "boke",
"description": "dkkd",
"visits": 4,
"praises_count": 0,
"watchers_count": 0,
"issues_count": 0,
"pull_requests_count": 0,
"forked_count": 0,
"is_public": true,
"mirror_url": "https://github.com/viletyy/monkey.git",
"type": 1,
"last_update_time": 1619685144,
"time_ago": "27天前",
"forked_from_project_id": null,
"open_devops": false,
"platform": "forge",
"author": {
"name": "测试组织",
"type": "Organization",
"login": "ceshi_org",
"image_url": "images/avatars/Organization/9?t=1612706073"
},
"category": {
"id": 3,
"name": "深度学习"
},
"language": {
"id": 2,
"name": "C"
}
}
]
}
```
<aside class="success">
Success Data.
</aside>
## 用户添加星标项目
用户添加星标项目
> 示例:
```shell
curl -X POST http://localhost:3000/api/users/yystopf/is_pinned_projects/pin.json
```
```javascript
await octokit.request('GET /api/users/:login/is_pinned_projects/pin.json')
```
### HTTP 请求
`POST /api/users/:login/is_pinned_projects/pin.json`
### 请求字段说明:
#### 同时设定多个星标项目
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|is_pinned_project_ids |array |设定为星标项目的id |
#### 只设定一个星标项目
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|is_pinned_project_id |integer |设定为星标项目的id |
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 星标项目展示排序
星标项目展示排序
> 示例:
```shell
curl -X PATCH http://localhost:3000/api/users/yystopf/is_pinned_projects/11.json
```
```javascript
await octokit.request('PATCH/PUT /api/users/:login/is_pinned_projects/:id.json')
```
### HTTP 请求
`PATCH/PUT /api/users/:login/is_pinned_projects/:id.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|pinned_projects.position |int |排序,数字越大排名越前 |
> 请求的JSON示例
```json
{
"pinned_project": {
"position": 1
}
}
```
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户近期活动统计
用户近期活动统计, 默认显示近一周的数据
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/statistics/activity.json
```
```javascript
await octokit.request('GET /api/users/:login/statistics/activity.json')
```
### HTTP 请求
`GET /api/users/:login/statistics/activity.json`
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|dates |array |时间 |
|issues_count |array |易修数量|
|pull_requests_count |array |合并请求数量|
|commtis_count |array |贡献数量|
> 返回的JSON示例:
```json
{
"dates": [
"2021.05.21",
"2021.05.22",
"2021.05.23",
"2021.05.24",
"2021.05.25",
"2021.05.26",
"2021.05.27",
"2021.05.28"
],
"issues_count": [
0,
0,
0,
0,
0,
0,
0,
0
],
"pull_requests_count": [
0,
0,
0,
0,
0,
0,
0,
0
],
"commits_count": [
0,
0,
0,
0,
0,
0,
0,
0
]
}
```
<aside class="success">
Success Data.
</aside>
## 获取用户贡献度
获取用户贡献度
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/headmaps.json
```
```javascript
await octokit.request('GET /api/users/:login/headmaps.json')
```
### HTTP 请求
`GET api/users/:login/headmaps.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|year |string |年份 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_contributions |int |所选时间内的总贡献度 |
|headmaps.date |string|时间|
|headmaps.contributions |int|贡献度|
> 返回的JSON示例:
```json
{
"total_contributions": 139,
"headmaps": [
{
"date": "2021-02-07",
"contributions": 1
},
{
"date": "2021-02-21",
"contributions": 13
},
{
"date": "2021-02-25",
"contributions": 5
},
{
"date": "2021-03-01",
"contributions": 2
},
{
"date": "2021-03-04",
"contributions": 1
},
{
"date": "2021-03-15",
"contributions": 9
},
{
"date": "2021-03-22",
"contributions": 14
},
{
"date": "2021-03-24",
"contributions": 1
},
{
"date": "2021-03-30",
"contributions": 11
},
{
"date": "2021-04-06",
"contributions": 1
},
{
"date": "2021-04-12",
"contributions": 1
},
{
"date": "2021-04-13",
"contributions": 2
},
{
"date": "2021-04-19",
"contributions": 3
},
{
"date": "2021-04-23",
"contributions": 37
},
{
"date": "2021-04-25",
"contributions": 2
},
{
"date": "2021-04-26",
"contributions": 6
},
{
"date": "2021-04-28",
"contributions": 1
},
{
"date": "2021-04-29",
"contributions": 18
},
{
"date": "2021-04-30",
"contributions": 9
},
{
"date": "2021-05-04",
"contributions": 1
},
{
"date": "2021-05-06",
"contributions": 1
}
]
}
```
<aside class="success">
Success Data.
</aside>
## 获取用户动态
获取用户动态
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/project_trends.json
```
```javascript
await octokit.request('GET /api/users/:login/project_trends.json')
```
### HTTP 请求
`GET api/users/:login/project_trends.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|date |string |日期,格式: 2021-05-28 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_count |int |所选时间内的总动态数 |
|project_trends.trend_type |string|动态类型Issue易修VersionRelease版本发布PullRequest合并请求|
|project_trends.action_type |string|操作类型|
|project_trends.trend_id |integer|动态id|
|project_trends.user_name |string|用户名称|
|project_trends.user_login |string|用户用户名|
|project_trends.user_avatar |string|用户头像|
|project_trends.action_time |string|操作时间|
|project_trends.name |string|动态标题|
> 返回的JSON示例:
```json
{
"total_count": 16,
"project_trends": [
{
"id": 27,
"trend_type": "Issue",
"action_type": "创建了工单",
"trend_id": 18,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "21天前",
"name": "31213123123",
"issue_type": "1",
"status_id": 2,
"priority_id": 4,
"created_at": "2021-05-07 15:39",
"updated_at": "2021-05-27 15:42",
"assign_user_name": "yystopf",
"assign_user_login": "yystopf",
"issue_journal_size": 1,
"issue_journals": []
},
{
"id": 8,
"trend_type": "VersionRelease",
"action_type": "创建了版本发布",
"trend_id": 8,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "24天前",
"name": "heihei1",
"tag_name": "v1.0",
"target_commitish": "master",
"tarball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.tar.gz",
"zipball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.zip",
"url": "http://localhost:10080/api/v1/repos/forgeceshiorg1/ceshi1/releases/84",
"version_gid": "84",
"created_at": "2021-05-04 12:04"
},
{
"id": 25,
"trend_type": "PullRequest",
"action_type": "关闭了合并请求",
"trend_id": 14,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "13",
"created_at": "2021-04-30 15:39"
},
{
"id": 24,
"trend_type": "PullRequest",
"action_type": "创建了合并请求",
"trend_id": 13,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "211212",
"created_at": "2021-04-30 15:37"
},
{
"id": 23,
"trend_type": "PullRequest",
"action_type": "创建了合并请求",
"trend_id": 12,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "奇偶哦iu",
"created_at": "2021-04-30 10:19"
},
{
"id": 22,
"trend_type": "PullRequest",
"action_type": "创建了合并请求",
"trend_id": 11,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "2112123",
"created_at": "2021-04-29 18:46"
},
{
"id": 21,
"trend_type": "PullRequest",
"action_type": "关闭了合并请求",
"trend_id": 10,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "23123",
"created_at": "2021-04-29 18:45"
},
{
"id": 20,
"trend_type": "PullRequest",
"action_type": "创建了合并请求",
"trend_id": 9,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "33",
"created_at": "2021-04-29 18:37"
},
{
"id": 19,
"trend_type": "PullRequest",
"action_type": "关闭了合并请求",
"trend_id": 8,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "28天前",
"name": "gggg",
"created_at": "2021-04-29 17:51"
},
{
"id": 16,
"trend_type": "Issue",
"action_type": "创建了工单",
"trend_id": 8,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "1个月前",
"name": "hjhkj",
"issue_type": "1",
"status_id": 1,
"priority_id": 2,
"created_at": "2021-04-19 10:52",
"updated_at": "2021-04-19 10:52",
"assign_user_name": null,
"assign_user_login": null,
"issue_journal_size": 0,
"issue_journals": []
},
{
"id": 7,
"trend_type": "VersionRelease",
"action_type": "创建了版本发布",
"trend_id": 7,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "1个月前",
"name": "v3.0.1",
"tag_name": "v3.0.1",
"target_commitish": "master",
"tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.tar.gz",
"zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.zip",
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/78",
"version_gid": "78",
"created_at": "2021-03-30 15:51"
},
{
"id": 6,
"trend_type": "VersionRelease",
"action_type": "创建了版本发布",
"trend_id": 6,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "1个月前",
"name": "v3.0.0",
"tag_name": "v3.0.0",
"target_commitish": "master",
"tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.tar.gz",
"zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.zip",
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/77",
"version_gid": "77",
"created_at": "2021-03-30 15:33"
},
{
"id": 5,
"trend_type": "VersionRelease",
"action_type": "创建了版本发布",
"trend_id": 5,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "1个月前",
"name": "v1.0.0",
"tag_name": "v1.0.0",
"target_commitish": "master",
"tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.tar.gz",
"zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.zip",
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/76",
"version_gid": "76",
"created_at": "2021-03-30 15:27"
},
{
"id": 2,
"trend_type": "VersionRelease",
"action_type": "创建了版本发布",
"trend_id": 2,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "2个月前",
"name": "vvvv",
"tag_name": "v1.1",
"target_commitish": "dev",
"tarball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.tar.gz",
"zipball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.zip",
"url": "http://localhost:10080/api/v1/repos/yystopf/virus_blog/releases/6",
"version_gid": "6",
"created_at": "2021-03-15 14:18"
},
{
"id": 2,
"trend_type": "PullRequest",
"action_type": "创建了合并请求",
"trend_id": 2,
"user_name": "yystopf",
"user_login": "yystopf",
"user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
"action_time": "3个月前",
"name": "444",
"created_at": "2021-02-25 17:31"
}
]
}
```
<aside class="success">
Success Data.
</aside>
## 用户开发能力
用户开发能力, 默认为所有时间下的开发能力
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/statistics/develop.json
```
```javascript
await octokit.request('GET /api/users/:login/statistics/develop.json')
```
### HTTP 请求
`GET /api/users/:login/statistics/develop.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|start_time |integer |时间戳开始时间格式1621526400|
|end_time |integer |时间戳结束时间格式1622131200|
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|influence |int |影响力 |
|contribution |int |贡献度|
|activity |int |活跃度|
|experience |int |项目经验|
|language |int |语言能力|
|languages_percent |float |语言百分比|
|each_language_score |int |各门语言分数|
> 返回的JSON示例:
```json
{
"platform": {
"influence": 61,
"contribution": 75,
"activity": 66,
"experience": 95,
"language": 87,
"languages_percent": {
"CSS": 0.03,
"C#": 0.13,
"Ruby": 0.04,
"Go": 0.05,
"C": 0.19,
"Java": 0.34,
"Python": 0.09,
"C+": 0.01,
"C++": 0.11,
"Scala": 0.01,
"HTML": 0.01
},
"each_language_score": {
"CSS": 71,
"C#": 86,
"Ruby": 75,
"Go": 77,
"C": 90,
"Java": 93,
"Python": 83,
"C+": 66,
"C++": 85,
"Scala": 66,
"HTML": 66
}
},
"user": {
"influence": 60,
"contribution": 72,
"activity": 65,
"experience": 88,
"language": 84,
"languages_percent": {
"C": 0.25,
"C#": 0.33,
"C++": 0.13,
"CSS": 0.08,
"Go": 0.04,
"HTML": 0.04,
"Java": 0.04,
"Ruby": 0.08
},
"each_language_score": {
"C": 81,
"C#": 84,
"C++": 75,
"CSS": 71,
"Go": 66,
"HTML": 66,
"Java": 66,
"Ruby": 71
}
}
}
```
<aside class="success">
Success Data.
</aside>
## 用户角色定位
用户角色定位,默认显示所有时间下的角色定位数据
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/statistics/role.json
```
```javascript
await octokit.request('GET /api/users/:login/statistics/role.json')
```
### HTTP 请求
`GET /api/users/:login/statistics/role.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|start_time |integer |时间戳开始时间格式1621526400|
|end_time |integer |时间戳结束时间格式1622131200|
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_projects_count |int |用户所有的项目数量 |
|role.object.count |int |用户该语言下的项目数量|
|role.object.percent |float |用户该语言下的项目占比|
> 返回的JSON示例:
```json
{
"total_projects_count": 27,
"role": {
"owner": {
"count": 24,
"percent": 0.89
},
"manager": {
"count": 1,
"percent": 0.04
},
"developer": {
"count": 2,
"percent": 0.07
},
"reporter": {
"count": 0,
"percent": 0.0
}
}
}
```
<aside class="success">
Success Data.
</aside>
## 用户专业定位
用户专业定位,默认显示所有时间下的专业定位数据
> 示例:
```shell
curl -X GET http://localhost:3000/api/users/yystopf/statistics/major.json
```
```javascript
await octokit.request('GET /api/users/:login/statistics/major.json')
```
### HTTP 请求
`GET /api/users/:login/statistics/major.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|start_time |integer |时间戳开始时间格式1621526400|
|end_time |integer |时间戳结束时间格式1622131200|
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|categories |int |用户项目分类 |
> 返回的JSON示例:
```json
{
"categories": [
"大数据",
"机器学习",
"深度学习",
"人工智能",
"智慧医疗",
"云计算"
]
}
```
<aside class="success">
Success Data.
</aside>
## 待办事项-用户通知信息
待办事项-用户通知信息

View File

@ -94,7 +94,7 @@ module TagChosenHelper
def render_issue_tags(project)
# project.issue_tags.last&.cache_key
cache_key = "all_issue_tags/#{project.issue_tags.maximum('updated_at')}"
cache_key = "project-#{project.id}/all_issue_tags/size-#{project.issue_tags.size}/#{project.issue_tags.maximum('updated_at')}"
Rails.cache.fetch(cache_key) do
project.issue_tags.select(:id, :name, :color).collect do |event|
@ -109,7 +109,7 @@ module TagChosenHelper
end
def render_cache_milestones(project)
cache_key = "all_milestones/#{project.versions.maximum('updated_on')}"
cache_key = "project-#{project.id}/all_milestones/size-#{project.versions.size}/#{project.versions.maximum('updated_on')}"
Rails.cache.fetch(cache_key) do
project.versions.select(:id, :name, :status).collect do |event|
@ -124,7 +124,7 @@ module TagChosenHelper
end
def render_cache_collaborators(project)
cache_key = "all_collaborators/#{project.all_collaborators.maximum('created_on')}"
cache_key = "project-#{project.id}/all_collaborators/size-#{project.all_collaborators.size}/#{project.all_collaborators.maximum('updated_on')}"
Rails.cache.fetch(cache_key) do
project.all_collaborators.order(created_on: :desc).collect do |user|
{

View File

@ -0,0 +1,14 @@
class ResetPlatformCacheJob < ApplicationJob
queue_as :cache
def perform
Cache::PlatformFollowCountService.new.reset
Cache::PlatformIssueCountService.new.reset
Cache::PlatformProjectCountService.new.reset
Cache::PlatformProjectForkCountService.new.reset
Cache::PlatformProjectLanguagesCountService.new.reset
Cache::PlatformProjectPraisesCountService.new.reset
Cache::PlatformProjectWatchersCountService.new.reset
Cache::PlatformPullrequestCountService.new.reset
end
end

View File

@ -0,0 +1,14 @@
class ResetUserCacheJob < ApplicationJob
queue_as :cache
def perform(user)
Cache::UserFollowCountService.new(user).reset
Cache::UserIssueCountService.new(user).reset
Cache::UserProjectCountService.new(user).reset
Cache::UserProjectForkCountService.new(user).reset
Cache::UserProjectLanguagesCountService.new(user).reset
Cache::UserProjectPraisesCountService.new(user).reset
Cache::UserProjectWatchersCountService.new(user).reset
Cache::UserPullrequestCountService.new(user).reset
end
end

View File

@ -5,7 +5,27 @@ class SyncMirroredRepositoryJob < ApplicationJob
repo = Repository.find_by(id: repo_id)
current_user = User.find_by(id: user_id)
return if repo.blank? || current_user.blank?
result = Gitea::Repository::SyncMirroredService.new(repo.owner.login, repo.identifier, token: current_user.gitea_token).call
# TODO
# 先同步镜像库
if repo.config_accelerator?
puts "[gitea-accelerator]: ###### 镜像库开始同步 ######"
result = Gitea::Accelerator::SyncMirroredService.call(repo.identifier)
puts "[gitea-accelerator]: ###### 镜像库同步状态为 #{result[:status]}"
# TODO 暂时解决从镜像库镜像动作时间先执行的问题
# 避免了Gitea::Repository::SyncMirroredService先执行后加速器Gitea::Accelerator::SyncMirroredService
# 再执行的导致Gitea::Repository::SyncMirroredService执行镜像不是最新代码的问题
sleep 3.seconds
end
sync_common!(repo, current_user)
end
def sync_common!(repo, user)
result = Gitea::Repository::SyncMirroredService.call(repo.owner.login,
repo.identifier, token: user.gitea_token)
repo&.mirror.set_status! if result[:status] === 200
end
end

View File

@ -16,4 +16,12 @@ class ApplicationRecord < ActiveRecord::Base
def allow_sync_to_trustie?
Rails.env.production? && EduSetting.get('host_name') == 'https://www.educoder.net'
end
def reset_user_cache_async_job(user)
ResetUserCacheJob.perform_later(user)
end
def reset_platform_cache_async_job
ResetPlatformCacheJob.perform_later
end
end

View File

@ -1,42 +1,42 @@
# == Schema Information
#
# Table name: attachments
#
# id :integer not null, primary key
# container_id :integer
# container_type :string(30)
# filename :string(255) default(""), not null
# disk_filename :string(255) default(""), not null
# filesize :integer default("0"), not null
# content_type :string(255) default("")
# digest :string(60) default(""), not null
# downloads :integer default("0"), not null
# author_id :integer default("0"), not null
# created_on :datetime
# description :text(65535)
# disk_directory :string(255)
# attachtype :integer default("1")
# is_public :integer default("1")
# copy_from :string(255)
# quotes :integer default("0")
# is_publish :integer default("1")
# publish_time :datetime
# resource_bank_id :integer
# unified_setting :boolean default("1")
# cloud_url :string(255) default("")
# course_second_category_id :integer default("0")
# delay_publish :boolean default("0")
# link :string(255)
# clone_id :integer
#
# Indexes
#
# index_attachments_on_author_id (author_id)
# index_attachments_on_clone_id (clone_id)
# index_attachments_on_container_id_and_container_type (container_id,container_type)
# index_attachments_on_created_on (created_on)
#
# == Schema Information
#
# Table name: attachments
#
# id :integer not null, primary key
# container_id :integer
# container_type :string(30)
# filename :string(255) default(""), not null
# disk_filename :string(255) default(""), not null
# filesize :integer default("0"), not null
# content_type :string(255) default("")
# digest :string(60) default(""), not null
# downloads :integer default("0"), not null
# author_id :integer default("0"), not null
# created_on :datetime
# description :text(65535)
# disk_directory :string(255)
# attachtype :integer default("1")
# is_public :integer default("1")
# copy_from :integer
# quotes :integer default("0")
# is_publish :integer default("1")
# publish_time :datetime
# resource_bank_id :integer
# unified_setting :boolean default("1")
# cloud_url :string(255) default("")
# course_second_category_id :integer default("0")
# delay_publish :boolean default("0")
#
# Indexes
#
# index_attachments_on_author_id (author_id)
# index_attachments_on_container_id_and_container_type (container_id,container_type)
# index_attachments_on_course_second_category_id (course_second_category_id)
# index_attachments_on_created_on (created_on)
# index_attachments_on_is_public (is_public)
# index_attachments_on_quotes (quotes)
#
class Attachment < ApplicationRecord
include BaseModel
include Publicable
@ -51,7 +51,7 @@ class Attachment < ApplicationRecord
# 二级目录
# belongs_to :course_second_category, optional: true
scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(concat(users.lastname, users.firstname)) LIKE :search",
scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) LIKE :search",
:search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? }
scope :by_keywords, -> (keywords) { where("filename LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank? }
scope :ordered, -> (opts = {}) { order("#{opts[:sort_type]} #{opts[:sort] == 1 ? 'asc': 'desc'}") }

View File

@ -39,15 +39,13 @@
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
# is_shixun_marker :boolean default("0")
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# platform :string(255) default("0")
# gitea_token :string(255)
# gitea_uid :integer
# is_shixun_marker :boolean default("0")
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
# gitea_token :string(255)
# platform :string(255)
#
# Indexes
#
@ -55,9 +53,8 @@
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
# index_users_on_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
# index_users_on_login (login)
# index_users_on_mail (mail)
# index_users_on_type (type)
#

View File

@ -9,6 +9,7 @@ module ProjectOperable
has_many :reporters, -> { joins(:roles).where(roles: { name: 'Reporter' }) }, class_name: 'Member'
has_many :writable_members, -> { joins(:roles).where.not(roles: {name: 'Reporter'}) }, class_name: 'Member'
has_many :team_projects, dependent: :destroy
has_many :teams, through: :team_projects, source: :team
end
def set_owner_permission(creator)

View File

@ -20,4 +20,12 @@ class ForkUser < ApplicationRecord
belongs_to :user
belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id
after_save :reset_cache_data
after_destroy :reset_cache_data
def reset_cache_data
self.reset_platform_cache_async_job
self.reset_user_cache_async_job(self.project.owner)
end
end

View File

@ -6,7 +6,7 @@
# tracker_id :integer not null
# project_id :integer not null
# subject :string(255) default(""), not null
# description :text(4294967295)
# description :text(65535)
# due_date :date
# category_id :integer
# status_id :integer not null
@ -14,6 +14,7 @@
# priority_id :integer not null
# fixed_version_id :integer
# author_id :integer not null
# lock_version :integer default("0"), not null
# created_on :datetime
# updated_on :datetime
# start_date :date
@ -27,7 +28,7 @@
# closed_on :datetime
# project_issues_index :integer
# issue_type :string(255)
# token :integer default("0")
# token :string(255)
# issue_tags_value :string(255)
# is_lock :boolean default("0")
# issue_classify :string(255)
@ -50,7 +51,7 @@
class Issue < ApplicationRecord
#issue_type 1为普通2为悬赏
belongs_to :project, :counter_cache => true
belongs_to :project, counter_cache: true, touch: true
belongs_to :tracker,optional: true
has_many :project_trends, as: :trend, dependent: :destroy
has_one :pull_request
@ -75,8 +76,13 @@ class Issue < ApplicationRecord
scope :issue_index_includes, ->{includes(:tracker, :priority, :version, :issue_status, :journals,:issue_tags,user: :user_extension)}
after_update :change_versions_count
after_destroy :update_closed_issues_count_in_project!
after_save :reset_cache_data
after_destroy :update_closed_issues_count_in_project!, :reset_cache_data
def reset_cache_data
self.reset_platform_cache_async_job
self.reset_user_cache_async_job(self.user)
end
def get_assign_user
User&.find_by_id(self.assigned_to_id) if self.assigned_to_id.present?

View File

@ -10,7 +10,6 @@
# sync_course :boolean default("0")
# sync_subject :boolean default("0")
# sync_shixun :boolean default("0")
# is_local :boolean default("0")
#
# Indexes
#

View File

@ -39,15 +39,13 @@
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
# is_shixun_marker :boolean default("0")
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# platform :string(255) default("0")
# gitea_token :string(255)
# gitea_uid :integer
# is_shixun_marker :boolean default("0")
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
# gitea_token :string(255)
# platform :string(255)
#
# Indexes
#
@ -55,9 +53,8 @@
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
# index_users_on_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
# index_users_on_login (login)
# index_users_on_mail (mail)
# index_users_on_type (type)
#

View File

@ -0,0 +1,22 @@
# == Schema Information
#
# Table name: pinned_projects
#
# id :integer not null, primary key
# user_id :integer
# project_id :integer
# position :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_pinned_projects_on_project_id (project_id)
# index_pinned_projects_on_user_id (user_id)
#
class PinnedProject < ApplicationRecord
belongs_to :user
belongs_to :project
end

View File

@ -1,26 +1,35 @@
# == Schema Information
#
# Table name: praise_treads
#
# id :integer not null, primary key
# user_id :integer not null
# praise_tread_object_id :integer
# praise_tread_object_type :string(255)
# praise_or_tread :integer default("1")
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# praise_tread (praise_tread_object_id,praise_tread_object_type)
#
# == Schema Information
#
# Table name: praise_treads
#
# id :integer not null, primary key
# user_id :integer not null
# praise_tread_object_id :integer
# praise_tread_object_type :string(255)
# praise_or_tread :integer default("1")
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# praise_tread (praise_tread_object_id,praise_tread_object_type)
#
class PraiseTread < ApplicationRecord
belongs_to :user
belongs_to :praise_tread_object, polymorphic: true, counter_cache: :praises_count
has_many :tidings, :as => :container, :dependent => :destroy
after_create :send_tiding
after_save :reset_cache_data
after_destroy :reset_cache_data
def reset_cache_data
self.reset_platform_cache_async_job
if self.praise_tread_object.is_a?(Project)
self.reset_user_cache_async_job(self.praise_tread_object&.owner)
end
end
def send_tiding
case self.praise_tread_object_type

View File

@ -1,76 +1,65 @@
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :text(4294967295)
# homepage :string(255) default("")
# is_public :boolean default("1"), not null
# parent_id :integer
# created_on :datetime
# updated_on :datetime
# identifier :string(255)
# status :integer default("1"), not null
# lft :integer
# rgt :integer
# inherit_members :boolean default("0"), not null
# project_type :integer default("0")
# hidden_repo :boolean default("0"), not null
# attachmenttype :integer default("1")
# user_id :integer
# dts_test :integer default("0")
# enterprise_name :string(255)
# organization_id :integer
# project_new_type :integer
# gpid :integer
# forked_from_project_id :integer
# forked_count :integer default("0")
# publish_resource :integer default("0")
# visits :integer default("0")
# hot :integer default("0")
# invite_code :string(255)
# qrcode :string(255)
# qrcode_expiretime :integer default("0")
# script :text(65535)
# training_status :integer default("0")
# rep_identifier :string(255)
# project_category_id :integer
# project_language_id :integer
# praises_count :integer default("0")
# watchers_count :integer default("0")
# issues_count :integer default("0")
# pull_requests_count :integer default("0")
# language :string(255)
# versions_count :integer default("0")
# issue_tags_count :integer default("0")
# closed_issues_count :integer default("0")
# open_devops :boolean default("0")
# gitea_webhook_id :integer
# open_devops_count :integer default("0")
# recommend :boolean default("0")
# platform :integer default("0")
# license_id :integer
# ignore_id :integer
# default_branch :string(255) default("master")
# website :string(255)
# lesson_url :string(255)
#
# Indexes
#
# index_projects_on_forked_from_project_id (forked_from_project_id)
# index_projects_on_identifier (identifier)
# index_projects_on_is_public (is_public)
# index_projects_on_lft (lft)
# index_projects_on_name (name)
# index_projects_on_platform (platform)
# index_projects_on_project_type (project_type)
# index_projects_on_recommend (recommend)
# index_projects_on_rgt (rgt)
# index_projects_on_status (status)
# index_projects_on_updated_on (updated_on)
#
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :text(65535)
# homepage :string(255) default("")
# is_public :boolean default("1"), not null
# parent_id :integer
# created_on :datetime
# updated_on :datetime
# identifier :string(255)
# status :integer default("1"), not null
# lft :integer
# rgt :integer
# inherit_members :boolean default("0"), not null
# project_type :integer default("0")
# hidden_repo :boolean default("0"), not null
# attachmenttype :integer default("1")
# user_id :integer
# dts_test :integer default("0")
# enterprise_name :string(255)
# organization_id :integer
# project_new_type :integer
# gpid :integer
# forked_from_project_id :integer
# forked_count :integer default("0")
# publish_resource :integer default("0")
# visits :integer default("0")
# hot :integer default("0")
# invite_code :string(255)
# qrcode :string(255)
# qrcode_expiretime :integer default("0")
# script :text(65535)
# training_status :integer default("0")
# rep_identifier :string(255)
# project_category_id :integer
# project_language_id :integer
# license_id :integer
# ignore_id :integer
# praises_count :integer default("0")
# watchers_count :integer default("0")
# issues_count :integer default("0")
# pull_requests_count :integer default("0")
#
# Indexes
#
# index_projects_on_forked_from_project_id (forked_from_project_id)
# index_projects_on_identifier (identifier)
# index_projects_on_is_public (is_public)
# index_projects_on_lft (lft)
# index_projects_on_name (name)
# index_projects_on_platform (platform)
# index_projects_on_project_type (project_type)
# index_projects_on_recommend (recommend)
# index_projects_on_rgt (rgt)
# index_projects_on_status (status)
# index_projects_on_updated_on (updated_on)
#
class Project < ApplicationRecord
include Matchable
@ -111,11 +100,13 @@ class Project < ApplicationRecord
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_one :project_detail, dependent: :destroy
has_many :team_projects, dependent: :destroy
has_many :project_units, dependent: :destroy
has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
has_many :pinned_projects, dependent: :destroy
has_many :has_pinned_users, through: :pinned_projects, source: :user
after_save :check_project_members
after_save :check_project_members, :reset_cache_data
after_destroy :reset_cache_data
scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
@ -123,6 +114,14 @@ class Project < ApplicationRecord
delegate :content, to: :project_detail, allow_nil: true
delegate :name, to: :license, prefix: true, allow_nil: true
def reset_cache_data
if changes[:user_id].present?
first_owner = Owner.find_by_id(changes[:user_id].first)
self.reset_user_cache_async_job(first_owner)
end
self.reset_platform_cache_async_job
self.reset_user_cache_async_job(self.owner)
end
def self.search_project(search)
ransack(name_or_identifier_cont: search)

View File

@ -8,6 +8,11 @@
# projects_count :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# ancestry :string(255)
#
# Indexes
#
# index_project_categories_on_ancestry (ancestry)
#
class ProjectCategory < ApplicationRecord

View File

@ -16,11 +16,6 @@
# head :string(255)
# base :string(255)
# issue_id :integer
# fork_project_id :integer
# is_original :boolean default("0")
# comments_count :integer default("0")
# commits_count :integer default("0")
# files_count :integer default("0")
#
class PullRequest < ApplicationRecord
@ -31,13 +26,21 @@ class PullRequest < ApplicationRecord
belongs_to :issue
belongs_to :user
belongs_to :project, :counter_cache => true
belongs_to :project, counter_cache: true, touch: true
# belongs_to :fork_project, foreign_key: :fork_project_id
has_many :pull_request_assigns, foreign_key: :pull_request_id
has_many :pull_request_tags, foreign_key: :pull_request_id
has_many :project_trends, as: :trend, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
after_save :reset_cache_data
after_destroy :reset_cache_data
def reset_cache_data
self.reset_platform_cache_async_job
self.reset_user_cache_async_job(self.user)
end
def fork_project
Project.find_by(id: self.fork_project_id)
end

View File

@ -27,6 +27,7 @@
#
# Indexes
#
# index_repositories_on_identifier (identifier)
# index_repositories_on_project_id (project_id)
# index_repositories_on_user_id (user_id)
#
@ -82,5 +83,8 @@ class Repository < ApplicationRecord
source_clone_url.blank? ? mirror_url : source_clone_url
end
def config_accelerator?
!source_clone_url.blank?
end
end

View File

@ -39,15 +39,13 @@
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
# is_shixun_marker :boolean default("0")
# admin_visitable :boolean default("0")
# collaborator :boolean default("0")
# platform :string(255) default("0")
# gitea_token :string(255)
# gitea_uid :integer
# is_shixun_marker :boolean default("0")
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
# gitea_token :string(255)
# platform :string(255)
#
# Indexes
#
@ -55,9 +53,8 @@
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
# index_users_on_login (login) UNIQUE
# index_users_on_mail (mail) UNIQUE
# index_users_on_phone (phone) UNIQUE
# index_users_on_login (login)
# index_users_on_mail (mail)
# index_users_on_type (type)
#
@ -164,11 +161,16 @@ class User < Owner
has_many :organization_users, dependent: :destroy
has_many :organizations, through: :organization_users
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
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
scope :like, lambda { |keywords|
sql = "CONCAT(lastname, firstname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR nickname LIKE :keyword"
sql = "CONCAT_WS(lastname, firstname, nickname) LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search"
where(sql, :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
}
@ -176,7 +178,9 @@ class User < Owner
attr_accessor :password, :password_confirmation
delegate :gender, :department_id, :school_id, :location, :location_city, :technical_title, to: :user_extension, allow_nil: true
delegate :description, :gender, :department_id, :school_id, :location, :location_city,
:show_email, :show_location, :show_department,
:technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true
before_save :update_hashed_password
after_create do
@ -195,6 +199,13 @@ class User < Owner
validate :validate_sensitive_string
validate :validate_password_length
# 用户参与的所有项目
def full_member_projects
normal_projects = Project.members_projects(self.id).to_sql
org_projects = Project.joins(teams: :team_users).where(team_users: {user_id: self.id}).to_sql
return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
end
def name
login
end

View File

@ -12,9 +12,7 @@
#
# Indexes
#
# index_user_actions_on_ip (ip)
# index_user_actions_on_user_id (user_id)
# index_user_actions_on_user_id_and_action_type (user_id,action_type)
# index_user_actions_on_ip (ip)
#
class UserAction < ApplicationRecord

View File

@ -10,13 +10,10 @@
# updated_at :datetime not null
# register_status :integer default("0")
# action_status :integer default("0")
# is_delete :boolean default("0")
# user_id :integer
#
# Indexes
#
# index_user_agents_on_ip (ip)
# index_user_agents_on_user_id (user_id)
# index_user_agents_on_ip (ip) UNIQUE
#
class UserAgent < ApplicationRecord

View File

@ -22,9 +22,12 @@
# school_id :integer
# description :string(255) default("")
# department_id :integer
# honor :text(65535)
# edu_background :integer
# edu_entry_year :integer
# province :string(255)
# city :string(255)
# custom_department :string(255)
# show_email :boolean default("0")
# show_location :boolean default("0")
# show_department :boolean default("0")
#
# Indexes
#
@ -33,25 +36,25 @@
# index_user_extensions_on_user_id (user_id)
#
class UserExtension < ApplicationRecord
# identity 0: 教师教授 1: 学生, 2: 专业人士, 3: 开发者
enum identity: { teacher: 0, student: 1, professional: 2, developer: 3, enterprise: 4, unselect: -1 }
belongs_to :user, touch: true
belongs_to :school, optional: true
# belongs_to :department, optional: true
# before_save :set_laboratory_school
def identity_text
I18n.t("user.identity.#{identity}")
end
private
def set_laboratory_school
# return unless new_record?
# self.school_id = Laboratory.current.school_id if school_id.blank? && !Laboratory.current.main_site?
end
end
class UserExtension < ApplicationRecord
# identity 0: 教师教授 1: 学生, 2: 专业人士, 3: 开发者
enum identity: { teacher: 0, student: 1, professional: 2, developer: 3, enterprise: 4, unselect: -1 }
belongs_to :user, touch: true
belongs_to :school, optional: true
# belongs_to :department, optional: true
# before_save :set_laboratory_school
def identity_text
I18n.t("user.identity.#{identity}")
end
private
def set_laboratory_school
# return unless new_record?
# self.school_id = Laboratory.current.school_id if school_id.blank? && !Laboratory.current.main_site?
end
end

View File

@ -24,7 +24,7 @@
#
class Version < ApplicationRecord
belongs_to :project, counter_cache: true
belongs_to :project, counter_cache: true, touch: true
has_many :issues, class_name: "Issue", foreign_key: "fixed_version_id"
belongs_to :user, optional: true

View File

@ -15,10 +15,24 @@
# watchers_user_id_type (user_id,watchable_type)
#
class Watcher < ApplicationRecord
belongs_to :user
belongs_to :watchable, polymorphic: true, counter_cache: :watchers_count
scope :watching_users, ->(watchable_id){ where("watchable_type = ? and user_id = ?",'User',watchable_id)}
end
class Watcher < ApplicationRecord
belongs_to :user
belongs_to :watchable, polymorphic: true, counter_cache: :watchers_count
scope :watching_users, ->(watchable_id){ where("watchable_type = ? and user_id = ?",'User',watchable_id)}
after_save :reset_cache_data
after_destroy :reset_cache_data
def reset_cache_data
if self.watchable.is_a?(User)
self.reset_user_cache_async_job(self.watchable)
end
if self.watchable.is_a?(Project)
self.reset_user_cache_async_job(self.watchable&.owner)
end
self.reset_platform_cache_async_job
end
end

View File

@ -26,7 +26,7 @@ class Admins::ApplyItemBankQuery < ApplicationQuery
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}%")
.where('CONCAT_WS(lastname,firstname, nickname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
end
custom_sort(applies, params[:sort_by], params[:sort_direction])

View File

@ -26,7 +26,7 @@ class Admins::ApplyUserAuthenticationQuery < ApplicationQuery
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}%")
.where('CONCAT_WS(lastname,firstname,nickname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
end
custom_sort(applies, params[:sort_by], params[:sort_direction])

View File

@ -19,7 +19,7 @@ class Admins::CourseListQuery < ApplicationQuery
case search_type
when "0"
course_lists = course_lists.joins(:user)
.where('CONCAT(lastname, firstname) like :keyword', keyword: "%#{keyword}%")
.where('CONCAT_WS(lastname, firstname, nickname) like :keyword', keyword: "%#{keyword}%")
when "1"
course_lists = course_lists.where('name like :keyword', keyword: "%#{keyword}%")
end

View File

@ -35,7 +35,7 @@ class Admins::CourseQuery < ApplicationQuery
# 关键字
keyword = params[:keyword].to_s.strip
if keyword
sql = 'CONCAT(lastname, firstname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword'
sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword'
courses = courses.joins(:teacher, :course_list).where(sql, keyword: "%#{keyword}%")
end

View File

@ -11,7 +11,7 @@ class Admins::LaboratoryShixunQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
like_sql = 'shixuns.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword'
laboratory_shixuns = laboratory_shixuns.joins(shixun: :user).where(like_sql, keyword: "%#{keyword}%")
end

View File

@ -11,7 +11,7 @@ class Admins::LaboratorySubjectQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
like_sql = 'subjects.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword'
laboratory_subjects = laboratory_subjects.joins(subject: :user).where(like_sql, keyword: "%#{keyword}%")
end

View File

@ -40,7 +40,7 @@ class Admins::SubjectQuery < ApplicationQuery
# 关键字
keyword = params[:keyword].to_s.strip
if keyword
sql = 'CONCAT(lastname, firstname) LIKE :keyword OR subjects.name LIKE :keyword'
sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR subjects.name LIKE :keyword'
subjects = subjects.joins(:user).where(sql, keyword: "%#{keyword}%")
end

View File

@ -30,14 +30,14 @@ class Admins::UserQuery < ApplicationQuery
# 关键字检索
keyword = params[:keyword].to_s.strip.presence
if keyword
sql = 'CONCAT(lastname, firstname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword'
sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword'
users = users.where(sql, keyword: "%#{keyword}%")
end
# 姓名
name = params[:name].to_s.strip.presence
if name.present?
users = users.where('CONCAT(lastname, firstname) LIKE :name', name: "%#{name}%")
users = users.where('CONCAT_WS(lastname, firstname, nickname) LIKE :name', name: "%#{name}%")
end
# 单位ID

View File

@ -53,10 +53,15 @@ class Projects::ListMyQuery < ApplicationQuery
q = projects.ransack(name_or_identifier_cont: params[:search])
scope = q.result.includes(:project_category, :project_language,:owner, :repository)
scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users)
sort = params[:sort_by] || "updated_on"
sort_direction = params[:sort_direction] || "desc"
scope.order("projects.#{sort} #{sort_direction}")
if params[:choosed].present? && params[:choosed].is_a?(Array)
scope.order("FIELD(id, #{params[:choosed].reverse.join(",")}) desc")
else
scope.order("projects.#{sort} #{sort_direction}")
end
end
end

View File

@ -10,7 +10,7 @@ class UserQuery < ApplicationQuery
# 真实姓名
if name = strip_param(:name)
users = users.where('LOWER(CONCAT(users.lastname, users.firstname)) LIKE ?', "%#{name.downcase}%")
users = users.where('LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) LIKE ?', "%#{name.downcase}%")
end
# 单位名称

View File

@ -84,7 +84,7 @@ class Admins::ImportUserService < ApplicationService
if data.identity.to_i == 1
users = users.where(user_extensions: { student_id: data.student_id })
else
users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT(users.lastname,users.firstname) = ?', data.name)
users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT_WS(users.lastname,users.firstname,users.nickname) = ?', data.name)
end
users.first

View File

@ -0,0 +1,41 @@
class Cache::PlatformFollowCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_follow_count
platform_follow_count
end
def reset
reset_platform_follow_count
platform_follow_count
end
private
def platform_follow_count_key
"platform-follow-count"
end
def platform_follow_count
$redis_cache.get(platform_follow_count_key).to_i
end
def set_platform_follow_count
if $redis_cache.exists(platform_follow_count_key)
$redis_cache.incrby(platform_follow_count_key, increment_count)
else
reset_platform_follow_count
end
end
def reset_platform_follow_count
$redis_cache.set(platform_follow_count_key, Watcher.where(watchable_type: 'User').count)
end
end

View File

@ -0,0 +1,41 @@
class Cache::PlatformIssueCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_issue_count
platform_issue_count
end
def reset
reset_platform_issue_count
platform_issue_count
end
private
def platform_issue_count_key
"platform-issue-count"
end
def platform_issue_count
$redis_cache.get(platform_issue_count_key).to_i
end
def set_platform_issue_count
if $redis_cache.exists(platform_issue_count_key)
$redis_cache.incrby(platform_issue_count_key, increment_count)
else
reset_platform_issue_count
end
end
def reset_platform_issue_count
$redis_cache.set(platform_issue_count_key, Issue.count)
end
end

View File

@ -0,0 +1,41 @@
class Cache::PlatformProjectCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_project_count
platform_project_count
end
def reset
reset_platform_project_count
platform_project_count
end
private
def platform_project_count_key
"platform-project-count"
end
def platform_project_count
$redis_cache.get(platform_project_count_key).to_i
end
def set_platform_project_count
if $redis_cache.exists(platform_project_count_key)
$redis_cache.incrby(platform_project_count_key, increment_count)
else
reset_platform_project_count
end
end
def reset_platform_project_count
$redis_cache.set(platform_project_count_key, Project.count)
end
end

View File

@ -0,0 +1,41 @@
class Cache::PlatformProjectForkCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_project_fork_count
platform_project_fork_count
end
def reset
reset_platform_project_fork_count
platform_project_fork_count
end
private
def platform_project_fork_count_key
"platform-project-fork-count"
end
def platform_project_fork_count
$redis_cache.get(platform_project_fork_count_key).to_i
end
def set_platform_project_fork_count
if $redis_cache.exists(platform_project_fork_count_key)
$redis_cache.incrby(platform_project_fork_count_key, increment_count)
else
reset_platform_project_fork_count
end
end
def reset_platform_project_fork_count
$redis_cache.set(platform_project_fork_count_key, ForkUser.count)
end
end

View File

@ -0,0 +1,57 @@
class Cache::PlatformProjectLanguagesCountService < ApplicationService
attr_reader :key, :increment_count
def initialize(key=nil, increment_count=0)
@key = key
@increment_count = increment_count
end
def call
set_platform_project_language_count
platform_project_language_count
end
def reset_by_key
reset_platform_project_language_count_by_key
platform_project_language_count
end
def reset
reset_platform_project_language_count
platform_project_language_count
end
private
def platform_project_language_count_key
"platform-project-language-count"
end
def platform_project_language_count
$redis_cache.hgetall(platform_project_language_count_key).transform_values(&:to_i)
end
def set_platform_project_language_count
if $redis_cache.hlen(platform_project_language_count_key) == 0
reset_platform_project_language_count
elsif $redis_cache.hget(platform_project_language_count_key, key).nil?
reset_platform_project_language_count_by_key
else
$redis_cache.hincrby(platform_project_language_count_key, key, increment_count)
end
end
def reset_platform_project_language_count_by_key
return if key.nil?
$redis_cache.hset(platform_project_language_count_key, key, Project.joins(:project_language).where(project_languages: {name: key}).count)
end
def reset_platform_project_language_count
Project.joins(:project_language).group("project_languages.name").count.each do |k, v|
$redis_cache.hset(platform_project_language_count_key, k, v)
end
end
end

View File

@ -0,0 +1,41 @@
class Cache::PlatformProjectPraisesCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_project_praises_count
platform_project_praises_count
end
def reset
reset_platform_project_praises_count
platform_project_praises_count
end
private
def platform_project_praises_count_key
"platform-project-praises-count"
end
def platform_project_praises_count
$redis_cache.get(platform_project_praises_count_key).to_i
end
def set_platform_project_praises_count
if $redis_cache.exists(platform_project_praises_count_key)
$redis_cache.incrby(platform_project_praises_count_key, increment_count)
else
reset_platform_project_praises_count
end
end
def reset_platform_project_praises_count
$redis_cache.set(platform_project_praises_count_key, PraiseTread.where(praise_tread_object_type: "Project").count)
end
end

View File

@ -0,0 +1,41 @@
class Cache::PlatformProjectWatchersCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_project_watchers_count
platform_project_watchers_count
end
def reset
reset_platform_project_watchers_count
platform_project_watchers_count
end
private
def platform_project_watchers_count_key
"platform-project-watchers-count"
end
def platform_project_watchers_count
$redis_cache.get(platform_project_watchers_count_key).to_i
end
def set_platform_project_watchers_count
if $redis_cache.exists(platform_project_watchers_count_key)
$redis_cache.incrby(platform_project_watchers_count_key, increment_count)
else
reset_platform_project_watchers_count
end
end
def reset_platform_project_watchers_count
$redis_cache.set(platform_project_watchers_count_key, Watcher.where(watchable_type: 'Project').count)
end
end

View File

@ -0,0 +1,41 @@
class Cache::PlatformPullrequestCountService < ApplicationService
attr_reader :increment_count
def initialize(increment_count=0)
@increment_count = increment_count
end
def call
set_platform_pullrequest_count
platform_pullrequest_count
end
def reset
reset_platform_pullrequest_count
platform_pullrequest_count
end
private
def platform_pullrequest_count_key
"platform-pullrequest-count"
end
def platform_pullrequest_count
$redis_cache.get(platform_pullrequest_count_key).to_i
end
def set_platform_pullrequest_count
if $redis_cache.exists(platform_pullrequest_count_key)
$redis_cache.incrby(platform_pullrequest_count_key, increment_count)
else
reset_platform_pullrequest_count
end
end
def reset_platform_pullrequest_count
$redis_cache.set(platform_pullrequest_count_key, PullRequest.count)
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserFollowCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_follow_count
user_follow_count
end
def reset
reset_user_follow_count
user_follow_count
end
private
def user_follow_count_key
"user-follow-count-#{user.id}"
end
def user_follow_count
$redis_cache.get(user_follow_count_key).to_i
end
def set_user_follow_count
if $redis_cache.exists(user_follow_count_key)
$redis_cache.incrby(user_follow_count_key, increment_count)
else
reset_user_follow_count
end
end
def reset_user_follow_count
return if user.nil?
$redis_cache.set(user_follow_count_key, Watcher.where(watchable: user).count)
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserIssueCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_issue_count
user_issue_count
end
def reset
reset_user_issue_count
user_issue_count
end
private
def user_issue_count_key
"user-issue-count-#{user.id}"
end
def user_issue_count
$redis_cache.get(user_issue_count_key).to_i
end
def set_user_issue_count
if $redis_cache.exists(user_issue_count_key)
$redis_cache.incrby(user_issue_count_key, increment_count)
else
reset_user_issue_count
end
end
def reset_user_issue_count
return if user.nil?
$redis_cache.set(user_issue_count_key, Issue.where(author_id: user.id).count)
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserProjectCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_project_count
user_project_count
end
def reset
reset_user_project_count
user_project_count
end
private
def user_project_count_key
"user-project-count-#{user.id}"
end
def user_project_count
$redis_cache.get(user_project_count_key).to_i
end
def set_user_project_count
if $redis_cache.exists(user_project_count_key)
$redis_cache.incrby(user_project_count_key, increment_count)
else
reset_user_project_count
end
end
def reset_user_project_count
return if user.nil?
$redis_cache.set(user_project_count_key, user.projects.count)
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserProjectForkCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_project_fork_count
user_project_fork_count
end
def reset
reset_user_project_fork_count
user_project_fork_count
end
private
def user_project_fork_count_key
"user-project-fork-count-#{user.id}"
end
def user_project_fork_count
$redis_cache.get(user_project_fork_count_key).to_i
end
def set_user_project_fork_count
if $redis_cache.exists(user_project_fork_count_key)
$redis_cache.incrby(user_project_fork_count_key, increment_count)
else
reset_user_project_fork_count
end
end
def reset_user_project_fork_count
return if user.nil?
$redis_cache.set(user_project_fork_count_key, ForkUser.joins(:project).where(projects: {user_id: user.id}).count)
end
end

View File

@ -0,0 +1,60 @@
class Cache::UserProjectLanguagesCountService < ApplicationService
attr_reader :user, :key, :increment_count
def initialize(user, key=nil, increment_count=0)
@user = user
@key = key
@increment_count = increment_count
end
def call
set_user_project_language_count
user_project_language_count
end
def reset_by_key
reset_user_project_language_count_by_key
user_project_language_count
end
def reset
reset_user_project_language_count
user_project_language_count
end
private
def user_project_language_count_key
"user-project-language-count-#{user.id}"
end
def user_project_language_count
$redis_cache.hgetall(user_project_language_count_key).transform_values(&:to_i)
end
def set_user_project_language_count
if $redis_cache.hlen(user_project_language_count_key) == 0
reset_user_project_language_count
elsif $redis_cache.hget(user_project_language_count_key, key).nil?
reset_user_project_language_count_by_key
else
$redis_cache.hincrby(user_project_language_count_key, key, increment_count)
end
end
def reset_user_project_language_count_by_key
return if user.nil?
return if key.nil?
$redis_cache.hset(user_project_language_count_key, key, user.projects.joins(:project_language).where(project_languages: {name: key}).count)
end
def reset_user_project_language_count
return if user.nil?
user.projects.joins(:project_language).group("project_languages.name").count.each do |k, v|
$redis_cache.hset(user_project_language_count_key, k, v)
end
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserProjectPraisesCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_project_praises_count
user_project_praises_count
end
def reset
reset_user_project_praises_count
user_project_praises_count
end
private
def user_project_praises_count_key
"user-project-praises-count-#{user.id}"
end
def user_project_praises_count
$redis_cache.get(user_project_praises_count_key).to_i
end
def set_user_project_praises_count
if $redis_cache.exists(user_project_praises_count_key)
$redis_cache.incrby(user_project_praises_count_key, increment_count)
else
reset_user_project_praises_count
end
end
def reset_user_project_praises_count
return if user.nil?
$redis_cache.set(user_project_praises_count_key, PraiseTread.where(praise_tread_object_type: 'Project', praise_tread_object_id: user.projects).count)
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserProjectWatchersCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_project_watchers_count
user_project_watchers_count
end
def reset
reset_user_project_watchers_count
user_project_watchers_count
end
private
def user_project_watchers_count_key
"user-project-watchers-count-#{user.id}"
end
def user_project_watchers_count
$redis_cache.get(user_project_watchers_count_key).to_i
end
def set_user_project_watchers_count
if $redis_cache.exists(user_project_watchers_count_key)
$redis_cache.incrby(user_project_watchers_count_key, increment_count)
else
reset_user_project_watchers_count
end
end
def reset_user_project_watchers_count
return if user.nil?
$redis_cache.set(user_project_watchers_count_key, Watcher.where(watchable_type: 'Project', watchable_id: user.projects).count)
end
end

View File

@ -0,0 +1,43 @@
class Cache::UserPullrequestCountService < ApplicationService
attr_reader :user, :increment_count
def initialize(user, increment_count=0)
@user = user
@increment_count = increment_count
end
def call
set_user_pullrequest_count
user_pullrequest_count
end
def reset
reset_user_pullrequest_count
user_pullrequest_count
end
private
def user_pullrequest_count_key
"user-pullrequest-count-#{user.id}"
end
def user_pullrequest_count
$redis_cache.get(user_pullrequest_count_key).to_i
end
def set_user_pullrequest_count
if $redis_cache.exists(user_pullrequest_count_key)
$redis_cache.incrby(user_pullrequest_count_key, increment_count)
else
reset_user_pullrequest_count
end
end
def reset_user_pullrequest_count
return if user.nil?
$redis_cache.set(user_pullrequest_count_key, PullRequest.where(user_id: user.id).count)
end
end

View File

@ -0,0 +1,96 @@
class Gitea::Accelerator::BaseService < ApplicationService
def post(url, params)
puts "[gitea] request params: #{params}"
puts "[gitea] access_username: #{access_username}"
puts "[gitea] access_password: #{access_password}"
conn.post do |req|
req.url full_url(url)
req.body = params.to_json
end
end
private
def conn
@client ||= begin
Faraday.new(url: domain) do |req|
req.request :url_encoded
req.headers['Content-Type'] = 'application/json'
req.response :logger # 显示日志
req.adapter Faraday.default_adapter
req.basic_auth(access_username, access_password)
end
end
@client
end
def base_url
accelerator["base_url"]
end
def domain
accelerator["domain"]
end
def api_url
[domain, base_url].join('')
end
def full_url(api_rest, action='post')
url = [api_url, api_rest].join('').freeze
url = action === 'get' ? url : URI.escape(url)
puts "[gitea] request url: #{url}"
url
end
def access_username
accelerator["access_key_id"]
end
def access_password
accelerator["access_key_secret"]
end
def access_uid
accelerator["access_admin_uid"]
end
def accelerator
Gitea.gitea_config[:accelerator]
end
def render_status(response)
puts "[gitea] response status: #{response.status}"
puts "[gitea] response body: #{response.body}"
case response.status
when 201
success
when 403
error('APIForbiddenError')
when 422
error('APIValidationError')
else
error("MigrateError")
end
end
def error(message)
{
status: :error,
message: message,
data: nil
}
end
def success(data=nil)
{
status: :success,
message: nil,
data: data
}
end
def check_accelerator!
accelerator.blank? || access_username.blank? || access_password.blank? || domain.blank?
end
end

View File

@ -1,4 +1,4 @@
class Gitea::Accelerator::MigrateService < ApplicationService
class Gitea::Accelerator::MigrateService < Gitea::Accelerator::BaseService
attr_reader :params
# params description:
@ -36,8 +36,8 @@ class Gitea::Accelerator::MigrateService < ApplicationService
render_status(response)
end
private
private
def request_params
{
uid: access_uid,
@ -52,97 +52,4 @@ class Gitea::Accelerator::MigrateService < ApplicationService
def url
"/repos/migrate".freeze
end
def post(url, params)
puts "[gitea] request params: #{params}"
puts "[gitea] access_username: #{access_username}"
puts "[gitea] access_password: #{access_password}"
conn.post do |req|
req.url full_url(url)
req.body = params.to_json
end
end
def conn
@client ||= begin
Faraday.new(url: domain) do |req|
req.request :url_encoded
req.headers['Content-Type'] = 'application/json'
req.response :logger # 显示日志
req.adapter Faraday.default_adapter
req.basic_auth(access_username, access_password)
end
end
@client
end
def base_url
accelerator["base_url"]
end
def domain
accelerator["domain"]
end
def api_url
[domain, base_url].join('')
end
def full_url(api_rest, action='post')
url = [api_url, api_rest].join('').freeze
url = action === 'get' ? url : URI.escape(url)
puts "[gitea] request url: #{url}"
url
end
def access_username
accelerator["access_key_id"]
end
def access_password
accelerator["access_key_secret"]
end
def access_uid
accelerator["access_admin_uid"]
end
def accelerator
Gitea.gitea_config[:accelerator]
end
def render_status(response)
puts "[gitea] response status: #{response.status}"
puts "[gitea] response body: #{response.body}"
case response.status
when 201
success
when 403
error('APIForbiddenError')
when 422
error('APIValidationError')
else
error("MigrateError")
end
end
def error(message)
{
status: :error,
message: message,
data: nil
}
end
def success(data=nil)
{
status: :success,
message: nil,
data: data
}
end
def check_accelerator!
accelerator.blank? || access_username.blank? || access_password.blank? || domain.blank?
end
end

View File

@ -0,0 +1,31 @@
# Sync a mirrored repository
class Gitea::Accelerator::SyncMirroredService < Gitea::Accelerator::BaseService
attr_reader :repo, :token
# repo *
# name of the repo to sync
# example:
# Gitea::Accelerator::SyncMirroredService.call(repo.identifier)
def initialize(repo, token=nil)
@repo = repo
@token = token
end
def call
return error('[gitea:] accelerator config missing') if check_accelerator!
response = post(url, request_params)
{status: response.status}
end
private
def request_params
Hash.new.merge(token: token).compact
end
def url
"/repos/#{access_username}/#{repo}/mirror-sync".freeze
end
end

View File

@ -0,0 +1,23 @@
class Gitea::User::HeadmapService < Gitea::ClientService
attr_reader :start_time, :end_time, :username
def initialize(username, start_time, end_time)
@username = username
@start_time = start_time
@end_time = end_time
end
def call
response = get(url, params)
render_response(response)
end
private
def params
Hash.new.merge(start: start_time, end: end_time)
end
def url
"/users/#{username}/heatmap".freeze
end
end

View File

@ -34,7 +34,7 @@ class Projects::TransferService < ApplicationService
def update_visit_teams
if new_owner.is_a?(Organization)
# 为包含组织所有项目的团队创建项目访问权限
new_owner.build_permit_team_projects(project.id)
new_owner.build_permit_team_projects!(project.id)
else
project.team_projects.each(&:destroy!)
end

View File

@ -1,4 +1,10 @@
json.name issue.try(:subject)
json.issue_type issue.issue_type == "1" ? "普通" : "悬赏"
json.status_id issue.try(:status_id)
json.issue_status issue.issue_status.try(:name)
json.priority issue.priority.try(:name)
json.priority_id issue.try(:priority_id)
json.version issue.version.try(:name)
json.created_at format_time(issue.try(:created_on))
json.updated_at format_time(issue.try(:updated_on))
json.assign_user_name issue&.get_assign_user.try(:show_real_name)

View File

@ -0,0 +1,17 @@
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.action_time time_from_now(trend.created_at)
if trend.trend_type == "Issue"
json.partial! "issues/simple_issue_item", locals: {issue: trend.trend}
elsif trend.trend_type == "VersionRelease"
json.partial! "version_releases/simple_version_release", locals: {version: trend.trend}
else
json.name trend.trend&.title
json.created_at format_time(trend.trend&.created_at)
end

View File

@ -9,25 +9,9 @@ json.limit @limit
json.project_trends_size @project_trends_size
json.project_trends do
json.array! @project_trends.to_a.each do |trend|
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)
if trend.trend_type == "Issue"
json.partial! "issues/simple_issue_item", locals: {issue: trend.trend}
elsif trend.trend_type == "VersionRelease"
json.partial! "version_releases/simple_version_release", locals: {version: trend.trend}
else
json.name trend.trend.title
json.created_at format_time(trend.trend.created_at)
end
#后续需要天际pullrequest 和 版本的内容
json.partial! "detail", trend: trend
end
end

View File

@ -5,6 +5,9 @@ json.name project.name
json.description Nokogiri::HTML(project.description).text
json.visits project.visits
json.praises_count project.praises_count.to_i
json.watchers_count project.watchers_count.to_i
json.issues_count project.issues_count.to_i
json.pull_requests_count project.pull_requests_count.to_i
json.forked_count project.forked_count.to_i
json.is_public project.is_public
json.mirror_url project.repository&.mirror_url
@ -14,6 +17,7 @@ json.time_ago time_from_now(project.updated_on)
json.forked_from_project_id project.forked_from_project_id
json.open_devops project.open_devops?
json.platform project.platform
json.is_pinned project.has_pinned_users.include?(current_user)
json.author do
if project.educoder?
project_educoder = project.project_educoder

View File

@ -48,7 +48,7 @@ if @result[:repo]
json.size replace_bytes_to_b(number_to_human_size(@result[:repo]['size'].to_i*1024))
json.ssh_url @result[:repo]['ssh_url']
json.clone_url @result[:repo]['clone_url']
json.default_branch @result[:repo]['default_branch']
json.default_branch @project.educoder? ? "master" : @result[:repo]['default_branch']
json.empty @result[:repo]['empty']
json.full_name @result[:repo]['full_name']
json.private @result[:repo]['private']

View File

@ -12,4 +12,5 @@ json.permission render_permission(current_user, @project)
json.is_transfering @project.is_transfering
json.transfer do
json.partial! "/users/user_simple", locals: {user: @project&.applied_transfer_project&.owner}
end
end
json.is_pinned @project.has_pinned_users.include?(current_user)

View File

@ -1,7 +1,20 @@
json.user_id user.id
json.login user.login
json.name user.full_name
json.username @user.full_name
json.real_name @user.real_name
json.grade user.grade
json.identity user&.user_extension&.identity
# json.email user.mail # 邮箱原则上不暴露的,如果实在需要的话只能对某些具体的接口公开
json.image_url url_to_avatar(user)
json.gender @user.gender
json.login @user.login
json.user_id @user.id
json.image_url url_to_avatar(@user)
json.admin @user.admin?
json.user_identity @user.identity
json.is_watch current_user&.watched?(@user)
json.watched_count @user.fan_count #粉丝
json.watching_count @user.follow_count #关注数
json.created_time format_time(@user.created_on)
json.email @user.show_email ? @user.mail : nil
json.province @user.show_location ? @user.province : nil
json.city @user.show_location ? @user.city : nil
json.custom_department @user.show_department ? @user.custom_department : nil
json.description @user.description

View File

@ -3,7 +3,7 @@ json.format_time target.created_at.strftime("%Y-%m-%d")
json.name user.try(:show_real_name)
json.login user.try(:login)
json.image_url url_to_avatar(user)
json.is_current_user current_user.try(:id) == target.user_id
json.is_current_user current_user.try(:id) == user.id
json.is_watch current_user&.watched?(user)

View File

@ -1,5 +1,6 @@
json.username @user.full_name
json.real_name @user.real_name
json.gender @user.gender
json.login @user.login
json.user_id @user.id
json.image_url url_to_avatar(@user)
@ -14,3 +15,9 @@ json.profile_completed @user.profile_completed?
json.professional_certification @user.professional_certification
json.devops_step @user.devops_step
json.ci_certification @user.ci_certification?
json.email @user.mail
json.province @user.province
json.city @user.city
json.custom_department @user.custom_department
json.description @user.description
json.(@user, :show_email, :show_department, :show_location)

View File

@ -0,0 +1,5 @@
json.total_contributions @headmaps.collect{|map| map["contributions"]}.reduce(0, :+)
json.headmaps @headmaps.each do |map|
json.date Time.at(map["timestamp"].to_i).strftime("%Y-%m-%d")
json.contributions map["contributions"]
end

View File

@ -0,0 +1,7 @@
json.total_count @is_pinned_projects.total_count
json.projects @is_pinned_projects.each do |project|
json.partial! "projects/project_detail", project: project&.project
json.id project.id
json.position project.position
json.project_id project.project_id
end

View File

@ -0,0 +1,4 @@
json.total_count @project_trends.total_count
json.project_trends @project_trends.each do |trend|
json.partial! "project_trends/detail", trend: trend
end

View File

@ -1,15 +1,4 @@
# json.partial! 'users/user', locals: { user: @user }
json.username @user.full_name
json.real_name @user.real_name
json.login @user.login
json.user_id @user.id
json.image_url url_to_avatar(@user)
json.admin @user.admin?
json.user_identity @user.identity
json.is_watch current_user&.watched?(@user)
json.watched_count @user.fan_count #粉丝
json.watching_count @user.follow_count #关注数
json.partial! 'users/user', locals: { user: @user }
json.undo_messages @waiting_applied_messages.size
json.undo_transfer_projects @common_applied_transfer_projects.size
json.undo_events @undo_events
@ -17,4 +6,10 @@ json.user_composes_count @user_composes_count
json.user_org_count @user_org_count
json.common_projects_count @projects_common_count
json.mirror_projects_count @projects_mirrior_count
json.sync_mirror_projects_count @projects_sync_mirrior_count
json.sync_mirror_projects_count @projects_sync_mirrior_count
json.created_time format_time(@user.created_on)
json.email @user.show_email ? @user.mail : nil
json.province @user.show_location ? @user.province : nil
json.city @user.show_location ? @user.city : nil
json.custom_department @user.show_department ? @user.custom_department : nil
json.description @user.description

View File

@ -0,0 +1,19 @@
json.platform do
json.influence @platform_influence
json.contribution @platform_contribution
json.activity @platform_activity
json.experience @platform_experience
json.language @platform_language
# json.languages_percent @platform_languages_percent
# json.each_language_score @platform_each_language_score
end
json.user do
json.influence @influence
json.contribution @contribution
json.activity @activity
json.experience @experience
json.language @language
json.languages_percent @languages_percent
json.each_language_score @each_language_score
end

View File

@ -0,0 +1,19 @@
json.total_projects_count @full_member_projects_count
json.role do
json.owner do
json.count @owner_projects_count
json.percent (@owner_projects_count.to_f/@full_member_projects_count).round(2)
end
json.manager do
json.count @manager_projects_count
json.percent (@manager_projects_count.to_f/@full_member_projects_count).round(2)
end
json.developer do
json.count @developer_projects_count
json.percent (@developer_projects_count.to_f/@full_member_projects_count).round(2)
end
json.reporter do
json.count @reporter_projects_count
json.percent (@reporter_projects_count.to_f/@full_member_projects_count).round(2)
end
end

View File

@ -0,0 +1 @@
json.partial! 'users/user', locals: { user: @user }

View File

@ -1,7 +1,7 @@
json.count @watchers_count
json.users do
json.array! @watchers do |watcher|
json.partial! "/users/watch_user_detail", locals: {target: watcher, user: watcher.watchable}
json.partial! "/users/watch_user_detail", target: watcher, user: watcher.watchable
end
# json.partial! "/users/watch_user_detail", collection: @watchers, as: :target
end

View File

@ -0,0 +1,3 @@
redis_config = Rails.application.config_for(:redis)
cache_url = redis_config["url"] || 'redis://localhost:6379'
$redis_cache = Redis.new(url: cache_url, db: 2)

View File

@ -15,7 +15,7 @@ zh-CN:
'refused': '已拒绝'
'agreed': '已同意'
trend:
Issue: 工单
Issue: 易修(Issue)
PullRequest: 合并请求
VersionRelease: 版本发布
create: 创建了

View File

@ -190,6 +190,7 @@ Rails.application.routes.draw do
post :remote_update
post :remote_login
post :remote_password
post :change_password
end
end
@ -265,6 +266,21 @@ Rails.application.routes.draw do
post :refuse
end
end
resources :headmaps, only: [:index]
resources :is_pinned_projects, only: [:index, :update] do
collection do
post :pin
end
end
resources :statistics, only: [:index] do
collection do
get :activity
get :develop
get :role
get :major
end
end
resources :project_trends, only: [:index]
resources :organizations, only: [:index]
# resources :projects, only: [:index]
# resources :subjects, only: [:index]

View File

@ -7,3 +7,4 @@
- [searchkick, 10]
- [notify, 100]
- [mailers, 101]
- [cache, 10]

95
db/init_data.sql Normal file
View File

@ -0,0 +1,95 @@
-- ----------------------------
-- Records of roles
-- ----------------------------
BEGIN;
INSERT INTO `roles` (`id`, `name`, `position`, `assignable`, `builtin`, `permissions`, `issues_visibility`) VALUES
(1, 'Non member', 1, 1, 1, '---\n- :upload_attachments\n- :memos_attachments_download\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :add_messages\n- :delete_own_messages\n- :view_calendar\n- :view_documents\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :comment_news\n- :browse_repository\n- :view_changesets\n- :view_time_entries\n- :view_wiki_pages\n- :view_wiki_edits\n', 'default'),
(2, 'Anonymous', 2, 1, 2, '---\n- :memos_attachments_download\n- :view_course_files\n- :view_calendar\n- :view_documents\n- :view_files\n- :view_gantt\n- :view_issues\n- :browse_repository\n- :view_changesets\n- :view_time_entries\n- :view_wiki_pages\n- :view_wiki_edits\n', 'default'),
(3, 'Manager', 3, 1, 0, '---\n- :add_project\n- :edit_project\n- :close_project\n- :select_project_modules\n- :manage_members\n- :manage_versions\n- :add_subprojects\n- :is_manager\n- :projects_attachments_download\n- :as_teacher\n- :add_course\n- :edit_course\n- :close_course\n- :select_course_modules\n- :view_course_journals_for_messages\n- :course_attachments_download\n- :view_course_files\n- :view_homework_attaches\n- :quote_project\n- :manage_boards\n- :add_messages\n- :edit_messages\n- :edit_own_messages\n- :delete_messages\n- :delete_own_messages\n- :view_calendar\n- :view_code_review\n- :add_code_review\n- :edit_code_review\n- :delete_code_review\n- :assign_code_review\n- :code_review_setting\n- :add_documents\n- :edit_documents\n- :delete_documents\n- :view_documents\n- :manage_files\n- :view_files\n- :view_gantt\n- :manage_categories\n- :view_issues\n- :add_issues\n- :edit_issues\n- :manage_issue_relations\n- :manage_subtasks\n- :set_issues_private\n- :set_own_issues_private\n- :add_issue_notes\n- :edit_issue_notes\n- :edit_own_issue_notes\n- :view_private_notes\n- :set_notes_private\n- :move_issues\n- :delete_issues\n- :manage_public_queries\n- :save_queries\n- :view_issue_watchers\n- :add_issue_watchers\n- :delete_issue_watchers\n- :manage_news\n- :comment_news\n- :manage_repository\n- :browse_repository\n- :view_changesets\n- :commit_access\n- :manage_related_issues\n- :log_time\n- :view_time_entries\n- :edit_time_entries\n- :edit_own_time_entries\n- :manage_project_activities\n- :manage_wiki\n- :rename_wiki_pages\n- :delete_wiki_pages\n- :view_wiki_pages\n- :export_wiki_pages\n- :view_wiki_edits\n- :edit_wiki_pages\n- :delete_wiki_pages_attachments\n- :protect_wiki_pages\n', 'all'),
(4, 'Developer', 5, 1, 0, '---\n- :add_project\n- :manage_versions\n- :projects_attachments_download\n- :add_course\n- :edit_course\n- :close_course\n- :select_course_modules\n- :view_course_journals_for_messages\n- :course_attachments_download\n- :view_course_files\n- :view_homework_attaches\n- :paret_in_homework\n- :select_contest_modules\n- :quote_project\n- :contest_attachments_download\n- :manage_contestnotifications\n- :notificationcomment_contestnotifications\n- :manage_boards\n- :add_messages\n- :edit_own_messages\n- :delete_own_messages\n- :view_calendar\n- :manage_files\n- :view_files\n- :view_gantt\n- :manage_categories\n- :view_issues\n- :add_issues\n- :edit_issues\n- :manage_issue_relations\n- :manage_subtasks\n- :set_issues_private\n- :set_own_issues_private\n- :add_issue_notes\n- :edit_issue_notes\n- :edit_own_issue_notes\n- :view_private_notes\n- :set_notes_private\n- :move_issues\n- :delete_issues\n- :manage_public_queries\n- :save_queries\n- :view_issue_watchers\n- :add_issue_watchers\n- :delete_issue_watchers\n- :manage_repository\n- :browse_repository\n- :view_changesets\n- :commit_access\n- :manage_related_issues\n', 'all'),
(5, 'Reporter', 4, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :edit_course\n- :close_course\n- :select_course_modules\n- :view_course_journals_for_messages\n- :course_attachments_download\n- :view_course_files\n- :view_homework_attaches\n- :manage_boards\n- :add_messages\n- :edit_own_messages\n- :delete_own_messages\n- :view_calendar\n- :view_code_review\n- :manage_files\n- :view_files\n- :view_gantt\n- :view_issues\n- :add_issues\n- :edit_issues\n- :add_issue_notes\n- :edit_own_issue_notes\n- :save_queries\n- :comment_news\n- :browse_repository\n- :view_wiki_pages\n- :export_wiki_pages\n- :view_wiki_edits\n- :edit_wiki_pages\n- :delete_wiki_pages_attachments\n', 'all'),
(7, 'TeachingAsistant', 6, 1, 0, '---\n- :add_project\n- :edit_project\n- :manage_members\n- :projects_attachments_download\n- :as_teacher\n- :add_course\n- :edit_course\n- :close_course\n- :select_course_modules\n- :view_course_journals_for_messages\n- :course_attachments_download\n- :view_course_files\n- :view_homework_attaches\n- :manage_boards\n- :add_messages\n- :edit_own_messages\n- :delete_messages\n- :delete_own_messages\n- :view_calendar\n- :view_documents\n- :manage_files\n- :view_files\n- :view_gantt\n- :view_issues\n- :add_issues\n- :add_issue_notes\n- :save_queries\n- :manage_news\n- :comment_news\n- :browse_repository\n- :view_changesets\n- :view_time_entries\n- :view_wiki_pages\n- :export_wiki_pages\n- :view_wiki_edits\n- :edit_wiki_pages\n- :delete_wiki_pages_attachments\n', 'default'),
(9, 'Teacher', 7, 1, 0, '---\n- :upload_attachments\n- :memos_attachments_download\n- :add_project\n- :edit_project\n- :close_project\n- :select_project_modules\n- :manage_members\n- :manage_versions\n- :add_subprojects\n- :projects_attachments_download\n- :as_teacher\n- :add_course\n- :edit_course\n- :close_course\n- :select_course_modules\n- :view_course_journals_for_messages\n- :course_attachments_download\n- :view_course_files\n- :view_homework_attaches\n- :manage_boards\n- :add_messages\n- :edit_messages\n- :edit_own_messages\n- :delete_messages\n- :delete_own_messages\n- :view_calendar\n- :add_documents\n- :edit_documents\n- :delete_documents\n- :view_documents\n- :manage_files\n- :view_files\n- :view_gantt\n- :manage_categories\n- :view_issues\n- :add_issues\n- :edit_issues\n- :manage_issue_relations\n- :manage_subtasks\n- :set_issues_private\n- :set_own_issues_private\n- :add_issue_notes\n- :edit_own_issue_notes\n- :view_private_notes\n- :set_notes_private\n- :move_issues\n- :delete_issues\n- :manage_public_queries\n- :save_queries\n- :view_issue_watchers\n- :add_issue_watchers\n- :delete_issue_watchers\n- :manage_news\n- :comment_news\n- :manage_repository\n- :browse_repository\n- :view_changesets\n- :commit_access\n- :manage_related_issues\n- :log_time\n- :view_time_entries\n- :edit_time_entries\n- :edit_own_time_entries\n- :manage_project_activities\n- :manage_wiki\n- :rename_wiki_pages\n- :delete_wiki_pages\n- :view_wiki_pages\n- :export_wiki_pages\n- :view_wiki_edits\n- :edit_wiki_pages\n- :delete_wiki_pages_attachments\n- :protect_wiki_pages\n', 'default'),
(10, 'Student', 9, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :as_student\n- :add_course\n- :edit_course\n- :close_course\n- :select_course_modules\n- :view_course_journals_for_messages\n- :course_attachments_download\n- :view_course_files\n- :view_homework_attaches\n- :paret_in_homework\n- :manage_boards\n- :add_messages\n- :edit_own_messages\n- :delete_own_messages\n- :view_calendar\n- :view_documents\n- :manage_files\n- :view_files\n- :view_gantt\n- :view_issues\n- :add_issues\n- :add_issue_notes\n- :save_queries\n- :comment_news\n- :browse_repository\n- :view_changesets\n- :view_time_entries\n- :view_wiki_pages\n- :view_wiki_edits\n', 'default'),
(11, 'orgManager', 8, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :add_messages\n- :delete_own_messages\n- :view_calendar\n- :view_documents\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :comment_news\n- :browse_repository\n- :view_changesets\n- :view_time_entries\n- :view_wiki_pages\n- :view_wiki_edits\n', 'default'),
(12, 'orgMember', 10, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :add_messages\n- :delete_own_messages\n- :view_calendar\n- :view_documents\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :comment_news\n- :browse_repository\n- :view_changesets\n- :view_time_entries\n- :view_wiki_pages\n- :view_wiki_edits\n', 'default'),
(13, 'ContestManager', 11, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :select_contest_modules\n- :quote_project\n- :contest_attachments_download\n- :manage_contestnotifications\n- :notificationcomment_contestnotifications\n- :manage_boards\n- :add_messages\n- :edit_messages\n- :edit_own_messages\n- :delete_messages\n- :delete_own_messages\n- :view_calendar\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :browse_repository\n- :view_changesets\n', 'default'),
(14, 'Judge', 12, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :select_contest_modules\n- :quote_project\n- :contest_attachments_download\n- :manage_contestnotifications\n- :notificationcomment_contestnotifications\n- :manage_boards\n- :add_messages\n- :edit_messages\n- :edit_own_messages\n- :delete_own_messages\n- :view_calendar\n- :manage_files\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :browse_repository\n- :view_changesets\n', 'default'),
(15, 'Contestant', 13, 1, 0, '---\n- :add_project\n- :projects_attachments_download\n- :add_course\n- :course_attachments_download\n- :view_course_files\n- :select_contest_modules\n- :quote_project\n- :contest_attachments_download\n- :notificationcomment_contestnotifications\n- :add_messages\n- :edit_own_messages\n- :delete_own_messages\n- :view_calendar\n- :manage_files\n- :view_files\n- :view_gantt\n- :view_issues\n- :save_queries\n- :browse_repository\n- :view_changesets\n', 'default');
COMMIT;
-- ----------------------------
-- Records of ci_languages
-- ----------------------------
BEGIN;
INSERT INTO `ci_languages` (`id`, `name`, `content`, `usage_amount`, `created_at`, `updated_at`, `cover_id`) VALUES
(1, 'C', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGdjYwogIGNvbW1hbmRzOgogIC0gLi9jb25maWd1cmUKICAtIG1h\na2UKICAtIG1ha2UgdGVzdA==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(2, 'C++', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGdjYwogIGNvbW1hbmRzOgogIC0gLi9jb25maWd1cmUKICAtIG1h\na2UKICAtIG1ha2UgdGVzdA==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(3, 'Docker', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGRvY2tlcjpkaW5kCiAgdm9sdW1lczoKICAtIG5hbWU6IGRvY2tl\ncnNvY2sKICAgIHBhdGg6IC92YXIvcnVuL2RvY2tlci5zb2NrCiAgICBjb21t\nYW5kczoKICAgIC0gZG9ja2VyIHBzIC1hCgp2b2x1bWVzOgotIG5hbWU6IGRv\nY2tlcnNvY2sKICBob3N0OgogICAgcGF0aDogL3Zhci9ydW4vZG9ja2VyLnNv\nY2s=\n', 0, '2020-10-21 10:14:22', '2020-11-26 01:35:20', 351688),
(4, 'Java', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IG1hdmVuOjMtamRrLTEwCiAgY29tbWFuZHM6CiAgLSBtdm4gaW5z\ndGFsbCAtRHNraXBUZXN0cz10cnVlIC1EbWF2ZW4uamF2YWRvYy5za2lwPXRy\ndWUgLUIgLVYKICAtIG12biB0ZXN0IC1C\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(5, 'R', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHItYmFzZQogIGNvbW1hbmRzOgogIC0gUiAtZSAnaW5zdGFsbC5w\nYWNrYWdlcyhjKCdwYWNrYWdlMScsJ3BhY2thZ2UyJykpJwogIC0gUiBDTUQg\nYnVpbGQgLg==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(6, 'Ruby', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHJ1YnkKICBjb21tYW5kczoKICAtIGJ1bmRsZSBpbnN0YWxsIC0t\nam9icz0zIC0tcmV0cnk9MwogIC0gcmFrZQ==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(7, 'PHP', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiBpbnN0YWxs\nCiAgaW1hZ2U6IGNvbXBvc2VyCiAgY29tbWFuZHM6CiAgLSBjb21wb3NlciBp\nbnN0YWxsCgotIG5hbWU6IHRlc3QKICBpbWFnZTogcGhwOjcKICBjb21tYW5k\nczoKICAtIHZlbmRvci9iaW4vcGhwdW5pdCAtLWNvbmZpZ3VyYXRpb24gY29u\nZmlnLnhtbA==\n', 0, '2020-10-21 10:14:22', '2020-11-26 01:37:04', 351690),
(8, 'Python', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHB5dGhvbgogIGNvbW1hbmRzOgogIC0gcGlwIGluc3RhbGwgLXIg\ncmVxdWlyZW1lbnRzLnR4dAogIC0gcHl0ZXN0\n', 0, '2020-10-21 10:14:22', '2020-11-26 01:40:00', 351693),
(9, 'MySQL', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IG15c3FsCiAgY29tbWFuZHM6CiAgLSBzbGVlcCAxNQogIC0gbXlz\ncWwgLXUgcm9vdCAtaCBkYXRhYmFzZSAtLWV4ZWN1dGU9J1NFTEVDVCBWRVJT\nSU9OKCk7JwoKc2VydmljZXM6Ci0gbmFtZTogZGF0YWJhc2UKICBpbWFnZTog\nbXlzcWwKICBlbnZpcm9ubWVudDoKICAgIE1ZU1FMX0FMTE9XX0VNUFRZX1BB\nU1NXT1JEOiAneWVzJwogICAgTVlTUUxfREFUQUJBU0U6IHRlc3Q=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(10, 'MongoDB', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiBwaW5nCiAg\naW1hZ2U6IG1vbmdvOjQKICBjb21tYW5kczoKICAtIHNsZWVwIDUKICAtIG1v\nbmdvIC0taG9zdCBtb25nbyAtLWV2YWwgJ2RiLnZlcnNpb24oKScKCnNlcnZp\nY2VzOgotIG5hbWU6IG1vbmdvCiAgaW1hZ2U6IG1vbmdvOjQKICBjb21tYW5k\nOiBbIC0tc21hbGxmaWxlcyBd\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(11, 'Clojure', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGNsb2p1cmUKICBjb21tYW5kczoKICAtIGxlaW4gdGVzdA==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(12, 'CouchDB', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGNvdWNoZGI6Mi4yCiAgY29tbWFuZHM6CiAgLSBzbGVlcCAxNQog\nIC0gY3VybCBodHRwOi8vZGF0YWJhc2U6NTk4NAoKc2VydmljZXM6Ci0gbmFt\nZTogZGF0YWJhc2UKICBpbWFnZTogY291Y2hkYjoyLjI=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(13, 'Crystal', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGNyeXN0YWxsYW5nL2NyeXN0YWwKICBjb21tYW5kczoKICAtIHNo\nYXJkcyBpbnN0YWxsCiAgLSBjcnlzdGFsIHNwZWMuMg==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(14, 'D', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGRsYW5ndWFnZS9kbWQKICBjb21tYW5kczoKICAtIGR1YiB0ZXN0\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(15, 'Dart', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGdvb2dsZS9kYXJ0CiAgY29tbWFuZHM6CiAgLSBwdWIgZ2V0CiAg\nLSBwdWIgcnVuIHRlc3Q=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(16, 'Docker (dind)', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGRvY2tlcjpkaW5kCiAgdm9sdW1lczoKICAtIG5hbWU6IGRvY2tl\ncnNvY2sKICAgIHBhdGg6IC92YXIvcnVuCiAgICBjb21tYW5kczoKICAgIC0g\nc2xlZXAgNSAjIGdpdmUgZG9ja2VyIGVub3VnaCB0aW1lIHRvIHN0YXJ0CiAg\nICAtIGRvY2tlciBwcyAtYQoKc2VydmljZXM6Ci0gbmFtZTogZG9ja2VyCiAg\naW1hZ2U6IGRvY2tlcjpkaW5kCiAgcHJpdmlsZWdlZDogdHJ1ZQogIHZvbHVt\nZXM6CiAgLSBuYW1lOiBkb2NrZXJzb2NrCiAgICBwYXRoOiAvdmFyL3J1bgoK\ndm9sdW1lczoKLSBuYW1lOiBkb2NrZXJzb2NrCiAgdGVtcDoge30=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(17, 'Elasticsearch', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGFscGluZTozLjgKICBjb21tYW5kczoKICAtIGFwayBhZGQgY3Vy\nbAogIC0gc2xlZXAgNDUKICAtIGN1cmwgaHR0cDovL2RhdGFiYXNlOjkyMDAK\nCnNlcnZpY2VzOgotIG5hbWU6IGRhdGFiYXNlCiAgaW1hZ2U6IGVsYXN0aWNz\nZWFyY2g6NS1hbHBpbmU=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(18, 'Elixir', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGVsaXhpcjoxLjUKICBjb21tYW5kczoKICAtIG1peCBsb2NhbC5y\nZWJhciAtLWZvcmNlCiAgLSBtaXggbG9jYWwuaGV4IC0tZm9yY2UKICAtIG1p\neCBkZXBzLmdldAogIC0gbWl4IHRlc3Q=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(19, 'Erlang', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGVybGFuZzoyMQogIGNvbW1hbmRzOgogIC0gcmViYXIgZ2V0LWRl\ncHMKICAtIHJlYmFyIGNvbXBpbGUKICAtIHJlYmFyIHNraXBfZGVwcz10cnVl\nIGV1bml0\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(20, '20.Go (with Gopath)', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0Cgp3b3Jrc3BhY2U6CiAgYmFzZTogL2dv\nCiAgcGF0aDogc3JjL2hlbGxvLXdvcmxkCgpzdGVwczoKLSBuYW1lOiB0ZXN0\nCiAgaW1hZ2U6IGdvbGFuZwogIGNvbW1hbmRzOgogIC0gZ28gZ2V0CiAgLSBn\nbyB0ZXN0\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(21, '21.Go (with Modules)', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGdvbGFuZwogIGNvbW1hbmRzOgogIC0gZ28gdGVzdAogIC0gZ28g\nYnVpbGQ=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(22, 'Gradle', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGdyYWRsZTpqZGsxMAogIGNvbW1hbmRzOgogIC0gZ3JhZGxlIGFz\nc2VtYmxlCiAgLSBncmFkbGUgY2hlY2s=\n', 0, '2020-10-21 10:14:22', '2020-11-26 01:36:17', 351689),
(23, 'Groovy', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGdyYWRsZToyLjUtamRrOAogIGNvbW1hbmRzOgogIC0gLi9ncmFk\nbGV3IGFzc2VtYmxlCiAgLSAuL2dyYWRsZXcgY2hlY2s=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(24, 'Haskell', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGhhc2tlbGwKICBjb21tYW5kczoKICAtIGNhYmFsIGluc3RhbGwg\nLS1vbmx5LWRlcGVuZGVuY2llcyAtLWVuYWJsZS10ZXN0cwogIC0gY2FiYWwg\nY29uZmlndXJlIC0tZW5hYmxlLXRlc3RzCiAgLSBjYWJhbCBidWlsZAogIC0g\nY2FiYWwgdGVzdA==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(25, 'Haxe', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IGhheGUKICBjb21tYW5kczoKICAtIGhheGVsaWIgaW5zdGFsbCBi\ndWlsZC5oeG1sCiAgLSBoYXhlIGJ1aWxkLmh4bWw=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(26, 'MariaDB', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IG1hcmlhZGIKICBjb21tYW5kczoKICAtIHNsZWVwIDE1CiAgLSBt\neXNxbCAtdSByb290IC1oIGRhdGFiYXNlIC0tZXhlY3V0ZT0nU0VMRUNUIFZF\nUlNJT04oKTsnCgpzZXJ2aWNlczoKLSBuYW1lOiBkYXRhYmFzZQogIGltYWdl\nOiBtYXJpYWRiCiAgZW52aXJvbm1lbnQ6CiAgICBNWVNRTF9BTExPV19FTVBU\nWV9QQVNTV09SRDogJ3llcycKICAgIE1ZU1FMX0RBVEFCQVNFOiB0ZXN0\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(27, 'Maven', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IG1hdmVuOjMtamRrLTEwCiAgY29tbWFuZHM6CiAgLSBtdm4gaW5z\ndGFsbCAtRHNraXBUZXN0cz10cnVlIC1EbWF2ZW4uamF2YWRvYy5za2lwPXRy\ndWUgLUIgLVYKICAtIG12biB0ZXN0IC1C\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(28, 'Memcached', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHVidW50dQogIGNvbW1hbmRzOgogIC0gYXB0LWdldCB1cGRhdGUg\nLXFxCiAgLSBhcHQtZ2V0IGluc3RhbGwgLXkgLXFxIHRlbG5ldCA+IC9kZXYv\nbnVsbAogIC0gKHNsZWVwIDE7IGVjaG8gJ3N0YXRzJzsgc2xlZXAgMjsgZWNo\nbyAncXVpdCc7KSB8IHRlbG5ldCBjYWNoZSAxMTIxMSB8fCB0cnVlCgpzZXJ2\naWNlczoKLSBuYW1lOiBjYWNoZQogIGltYWdlOiBtZW1jYWNoZWQ6YWxwaW5l\nCiAgY29tbWFuZDogWyAtdnYgXQ==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(29, 'Nats', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHJ1Ynk6MgogIGNvbW1hbmRzOgogIC0gZ2VtIGluc3RhbGwgbmF0\ncwogIC0gbmF0cy1wdWIgLXMgdGNwOi8vbmF0czo0MjIyIGdyZWV0aW5nICdo\nZWxsbycKICAtIG5hdHMtcHViIC1zIHRjcDovL25hdHM6NDIyMiBncmVldGlu\nZyAnd29ybGQnCgpzZXJ2aWNlczoKLSBuYW1lOiBuYXRzCiAgaW1hZ2U6IG5h\ndHM6MS4zLjA=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(30, 'Node', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IG5vZGUKICBjb21tYW5kczoKICAtIG5wbSBpbnN0YWxsCiAgLSBu\ncG0gdGVzdA==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(31, 'Perl', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHBlcmwKICBjb21tYW5kczoKICAtIGNwYW5tIC0tcXVpZXQgLS1p\nbnN0YWxsZGVwcyAtLW5vdGVzdCAuCiAgLSBwZXJsIEJ1aWxkLlBMCiAgLSAu\nL0J1aWxkIHRlc3Q=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(32, 'Postgres', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHBvc3RncmVzOjktYWxwaW5lCiAgY29tbWFuZHM6CiAgLSBwc3Fs\nIC1VIHBvc3RncmVzIC1kIHRlc3QgLWggZGF0YWJhc2UKCnNlcnZpY2VzOgot\nIG5hbWU6IGRhdGFiYXNlCiAgaW1hZ2U6IHBvc3RncmVzOjktYWxwaW5lCiAg\nZW52aXJvbm1lbnQ6CiAgICBQT1NUR1JFU19VU0VSOiBwb3N0Z3JlcwogICAg\nUE9TVEdSRVNfREI6IHRlc3Q=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(33, 'Redis', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHJlZGlzCiAgY29tbWFuZHM6CiAgLSBzbGVlcCA1CiAgLSByZWRp\ncy1jbGkgLWggcmVkaXMgcGluZwogIC0gcmVkaXMtY2xpIC1oIHJlZGlzIHNl\ndCBGT08gYmFyCiAgLSByZWRpcy1jbGkgLWggcmVkaXMgZ2V0IEZPTwoKc2Vy\ndmljZXM6Ci0gbmFtZTogcmVkaXMKICBpbWFnZTogcmVkaXM=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(34, 'RethinkDB', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IG5vZGU6OQogIGNvbW1hbmRzOgogIC0gbnBtIGluc3RhbGwgLXMg\nLWcgcmVjbGkKICAtIHJlY2xpIC1oIGRhdGFiYXNlIC1qICdyLmRiKCdyZXRo\naW5rZGInKS50YWJsZSgnc3RhdHMnKScKCnNlcnZpY2VzOgotIG5hbWU6IGRh\ndGFiYXNlCiAgaW1hZ2U6IHJldGhpbmtkYjoyCiAgY29tbWFuZDogWyByZXRo\naW5rZGIsIC0tYmluZCwgYWxsIF0=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(35, 'Rust', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHJ1c3Q6MS4zMAogIGNvbW1hbmRzOgogIC0gY2FyZ28gYnVpbGQg\nLS12ZXJib3NlIC0tYWxsCiAgLSBjYXJnbyB0ZXN0IC0tdmVyYm9zZSAtLWFs\nbA==\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(36, 'Swift', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHN3aWZ0OjQKICBjb21tYW5kczoKICAtIHN3aWZ0IGJ1aWxkCiAg\nLSBzd2lmdCB0ZXN0\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL),
(37, 'Vault', 'CmtpbmQ6IHBpcGVsaW5lCm5hbWU6IGRlZmF1bHQKCnBsYXRmb3JtOgogIG9z\nOiBsaW51eAogIGFyY2g6IGFybTY0CgpzdGVwczoKLSBuYW1lOiB0ZXN0CiAg\naW1hZ2U6IHZhdWx0OjEuMC4wLWJldGEyCiAgZW52aXJvbm1lbnQ6CiAgICBW\nQVVMVF9BRERSOiBodHRwOi8vdmF1bHQ6ODIwMAogICAgVkFVTFRfVE9LRU46\nIGR1bW15CiBjb21tYW5kczoKIC0gc2xlZXAgNQogLSB2YXVsdCBrdiBwdXQg\nc2VjcmV0L215LXNlY3JldCBteS12YWx1ZT1zM2NyM3QKIC0gdmF1bHQga3Yg\nZ2V0IHNlY3JldC9teS1zZWNyZXQKCnNlcnZpY2VzOgotIG5hbWU6IHZhdWx0\nCiAgaW1hZ2U6IHZhdWx0OjEuMC4wLWJldGEyCiAgZW52aXJvbm1lbnQ6CiAg\nICBWQVVMVF9ERVZfUk9PVF9UT0tFTl9JRDogZHVtbXk=\n', 0, '2020-10-21 10:14:22', '2020-10-21 10:14:22', NULL);
COMMIT;
-- ----------------------------
-- Records of ci_templates
-- ----------------------------
BEGIN;
INSERT INTO `ci_templates` (`id`, `template_name`, `stage_type`, `category`, `content`, `created_at`, `updated_at`, `parent_category`, `login`) VALUES
(2, 'linux/amd64', 'init', '初始化', 'kind: pipeline\r\ntype: docker\r\nname: {name}\r\nplatform:\r\n os: linux\r\n arch: amd64', '2021-01-12 02:44:23', '2021-02-02 10:51:36', '初始化', 'admin'),
(3, 'linux/arm64', 'init', '初始化', 'kind: pipeline\r\ntype: docker\r\nname: {name}\r\nplatform:\r\n os: linux\r\n arch: arm64', '2021-01-12 02:45:17', '2021-02-02 10:51:47', '初始化', 'admin'),
(4, 'maven', 'build', 'Java', '- name: maven\r\n image: maven:3-jdk-10\r\n commands:\r\n - mvn install -DskipTests=true', '2021-01-12 02:53:29', '2021-01-12 02:53:29', '编译构建', 'admin'),
(5, 'maven单元测试', 'customize', 'Java', '- name: maven\r\n image: maven:3-jdk-10\r\n commands:\r\n - mvn test', '2021-01-12 02:53:29', '2021-01-12 02:53:29', '单元测试', 'admin'),
(6, 'golang单元测试', 'customize', 'Golang', '- name: golang单元测试\r\n image: golang\r\n commands:\r\n - go test', '2021-01-12 03:03:35', '2021-01-12 03:03:35', '单元测试', 'admin'),
(8, 'gradle', 'build', 'Java', '- name: gradle\r\n image: gradle:jdk10\r\n commands:\r\n - gradle build -x test', '2021-01-12 03:05:33', '2021-01-12 03:05:33', '编译构建', 'admin'),
(9, 'gradle单元测试', 'customize', 'Java', '- name: gradle\r\n image: gradle:jdk10\r\n commands:\r\n - gradle test', '2021-01-12 03:05:33', '2021-01-12 03:05:33', '单元测试', 'admin'),
(10, '远程主机部署', 'deploy', '部署', '# 需要将软件包与部署脚本提前上传到远程主机(见文件上传模板)\r\n# host、username、password可在参数管理中配置\r\n- name: 远程主机部署\r\n image: appleboy/drone-ssh\r\n settings:\r\n host: \r\n from_secret: ip\r\n username: \r\n from_secret: name\r\n password: \r\n from_secret: pwd\r\n port: 22\r\n script:\r\n - chmod +x /home/deploy.sh\r\n - ./home/deploy.sh', '2021-01-12 03:32:46', '2021-03-05 06:22:02', '部署', 'admin'),
(11, '远程命令', 'customize', '其他', '# host、username、password可在参数管理中配置\r\n- name: 远程命令\r\n image: appleboy/drone-ssh\r\n settings:\r\n host: \r\n from_secret: ip\r\n username: \r\n from_secret: name\r\n password: \r\n from_secret: pwd\r\n port: 22\r\n script:\r\n - echo ''hello world!''', '2021-01-12 03:40:38', '2021-03-05 06:19:44', '其他', 'admin'),
(12, '上传文件', 'customize', '其他', '# 本模板示例为上传软件包和部署脚本到home目录\r\n# host、username、password可在参数管理中配置\r\n- name: 上传文件\r\n image: appleboy/drone-scp\r\n settings:\r\n host: \r\n from_secret: ip\r\n username: \r\n from_secret: name\r\n password: \r\n from_secret: pwd\r\n port: 22\r\n target: /home\r\n source: \r\n - target/*.jar\r\n - deploy.sh', '2021-01-12 03:40:55', '2021-03-05 06:22:22', '其他', 'admin'),
(17, 'make-c', 'build', 'C', '- name: 编译\r\n image: gcc\r\n commands:\r\n - ./configure\r\n - make', '2021-01-15 01:19:38', '2021-02-02 10:52:15', '编译构建', 'admin'),
(19, 'make-c++', 'build', 'C++', '- name: 编译构建\r\n image: gcc\r\n commands:\r\n - ./configure\r\n - make', '2021-01-15 01:21:05', '2021-01-15 01:21:05', '编译构建', 'admin'),
(20, 'python', 'build', 'Python', '- name: 编译构建\r\n image: python\r\n commands:\r\n - pip install -r requirements.txt', '2021-01-15 01:22:36', '2021-01-15 01:22:36', '编译构建', 'admin'),
(21, 'Docker', 'build', 'Docker', '# 构建Docker镜像并推送到仓库\r\n# 定义镜像Hub路径以及账号密码\r\n- name: Docker镜像构建\r\n image: plugins/docker\r\n settings:\r\n username: username\r\n password: pwd\r\n repo: repoUrl\r\n tags: latest', '2021-01-15 01:23:16', '2021-02-03 03:40:49', '编译构建', 'admin'),
(22, '空白模板', 'customize', 'customize', '', '2021-01-15 02:53:02', '2021-01-15 02:53:02', '其他', 'admin'),
(25, 'Go (with Gopath)', 'build', 'Go', '- name: golang\r\n image: golang\r\n commands:\r\n - go get\r\n - go test', '2021-02-03 00:57:32', '2021-02-03 03:36:15', '编译构建', 'admin'),
(26, 'PHP', 'build', 'PHP', '- name: install\r\n image: composer\r\n commands:\r\n - composer install', '2021-02-03 00:59:15', '2021-02-03 00:59:15', '编译构建', 'admin'),
(27, 'Ruby', 'build', 'Ruby', '- name: ruby\r\n image: ruby\r\n commands:\r\n - bundle install --jobs=3 --retry=3\r\n - rake', '2021-02-03 00:59:52', '2021-02-03 00:59:52', '编译构建', 'admin'),
(29, 'Go (with Modules)', 'build', 'Go', '- name: test\r\n image: golang\r\n commands:\r\n - go test\r\n - go build', '2021-02-03 03:35:45', '2021-02-03 03:35:45', '编译构建', 'admin'),
(30, 'Node', 'build', 'Node', '- name: Node编译\r\n image: node\r\n commands:\r\n - npm install\r\n - npm test', '2021-02-03 03:37:31', '2021-02-03 03:37:31', '编译构建', 'admin'),
(31, 'Perl ', 'build', 'Perl', '- name: perl\r\n image: perl\r\n commands:\r\n - cpanm --quiet --installdeps --notest .\r\n - perl Build.PL\r\n - ./Build test', '2021-02-03 03:38:06', '2021-02-03 03:38:06', '编译构建', 'admin'),
(32, 'Rust ', 'build', 'Rust', '- name: rust\r\n image: rust:1.30\r\n commands:\r\n - cargo build --verbose --all\r\n - cargo test --verbose --all', '2021-02-03 03:38:37', '2021-02-03 03:38:37', '编译构建', 'admin'),
(33, 'test', 'init', '初始化', 'qqqq', '2021-02-04 09:02:42', '2021-02-04 09:02:42', '初始化', 'admin');
COMMIT;

View File

@ -0,0 +1,11 @@
class CreatePinnedProjects < ActiveRecord::Migration[5.2]
def change
create_table :pinned_projects do |t|
t.references :user
t.references :project
t.integer :position, default: 0
t.timestamps
end
end
end

View File

@ -0,0 +1,8 @@
class AddFieldsToUserExtensions < ActiveRecord::Migration[5.2]
def change
add_column :user_extensions, :province, :string # 省份
add_column :user_extensions, :city, :string # 城市
add_column :user_extensions, :custom_department, :string #自己填写的单位名称
# remove_column :users, :description
end
end

View File

@ -0,0 +1,7 @@
class AddShowConditionToUserExtensions < ActiveRecord::Migration[5.2]
def change
add_column :user_extensions, :show_email, :boolean, default: false
add_column :user_extensions, :show_location, :boolean, default: false
add_column :user_extensions, :show_department, :boolean, default: false
end
end

Some files were not shown because too many files have changed in this diff Show More