| @@ -10,7 +10,7 @@ | |||
| # Ignore lock config file | |||
| *.log | |||
| .env | |||
| # mac | |||
| *.DS_Store | |||
| .bashrc | |||
| @@ -84,4 +84,5 @@ redis_data/ | |||
| dump.rdb | |||
| .tags* | |||
| ceshi_user.xlsx | |||
| public/trace_task_results | |||
| public/trace_task_results | |||
| public/项目活跃度排行.xls | |||
| @@ -141,4 +141,4 @@ gem 'doorkeeper' | |||
| gem 'doorkeeper-jwt' | |||
| gem 'gitea-client', '~> 1.4.2' | |||
| gem 'gitea-client', '~> 1.4.6' | |||
| @@ -201,12 +201,12 @@ class AccountsController < ApplicationController | |||
| return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked? | |||
| login_control = LimitForbidControl::UserLogin.new(@user) | |||
| return normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid? | |||
| return normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid? | |||
| password_ok = @user.check_password?(params[:password].to_s) | |||
| unless password_ok | |||
| if login_control.remain_times-1 == 0 | |||
| normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") | |||
| normal_status(-2, "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") | |||
| else | |||
| normal_status(-2, "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会") | |||
| end | |||
| @@ -222,6 +222,7 @@ class AccountsController < ApplicationController | |||
| end | |||
| def change_password | |||
| return render_error("两次输入的密码不一致") if params[:password].to_s != params[:new_password_repeat].to_s | |||
| @user = User.find_by(login: params[:login]) | |||
| return render_error("此用户禁止修改密码!") if @user.id.to_i === 104691 | |||
| return render_error("未找到相关用户!") if @user.blank? | |||
| @@ -21,13 +21,75 @@ class Admins::DashboardsController < Admins::BaseController | |||
| weekly_project_ids = (CommitLog.where(created_at: current_week).pluck(:project_id).uniq + Issue.where(created_on: current_week).pluck(:project_id).uniq).uniq | |||
| month_project_ids = (CommitLog.where(created_at: current_month).pluck(:project_id).uniq + Issue.where(created_on: current_month).pluck(:project_id).uniq).uniq | |||
| @day_active_project_count = Project.where(updated_on: today).or(Project.where(id: day_project_ids)).count | |||
| @weekly_active_project_count = Project.where(updated_on: current_week).or(Project.where(id: weekly_project_ids)).count | |||
| @month_active_project_count = Project.where(updated_on: current_month).or(Project.where(id: month_project_ids)).count | |||
| @weekly_active_project_count = Rails.cache.fetch("dashboardscontroller:weekly_active_project_count", expires_in: 10.minutes) do | |||
| Project.where(updated_on: current_week).or(Project.where(id: weekly_project_ids)).count | |||
| end | |||
| @month_active_project_count = Rails.cache.fetch("dashboardscontroller:month_active_project_count", expires_in: 1.hours) do | |||
| Project.where(updated_on: current_month).or(Project.where(id: month_project_ids)).count | |||
| end | |||
| # 新增项目数 | |||
| @day_new_project_count = Project.where(created_on: today).count | |||
| @weekly_new_project_count = Project.where(created_on: current_week).count | |||
| @month_new_project_count = Project.where(created_on: current_month).count | |||
| @day_new_project_count = Rails.cache.fetch("dashboardscontroller:day_new_project_count", expires_in: 10.minutes) do | |||
| Project.where(created_on: today).count | |||
| end | |||
| @weekly_new_project_count = Rails.cache.fetch("dashboardscontroller:weekly_new_project_count", expires_in: 10.minutes) do | |||
| Project.where(created_on: current_week).count | |||
| end | |||
| @month_new_project_count = Rails.cache.fetch("dashboardscontroller:month_new_project_count", expires_in: 1.hours) do | |||
| Project.where(created_on: current_month).count | |||
| end | |||
| # 总的平台用户数 | |||
| # 总的平台项目数 | |||
| # 总的平台组织数 | |||
| # 总的平台Issue数、评论数、PR数、Commit数 | |||
| @user_count = Rails.cache.fetch("dashboardscontroller:platform:user_count", expires_in: 1.days) do | |||
| User.count | |||
| end | |||
| @project_count = Rails.cache.fetch("dashboardscontroller:platform:project_count", expires_in: 1.days) do | |||
| Project.count | |||
| end | |||
| @organization_count = Rails.cache.fetch("dashboardscontroller:platform:organization_count", expires_in: 1.days) do | |||
| Organization.count | |||
| end | |||
| @issue_count = Rails.cache.fetch("dashboardscontroller:platform:issue_count", expires_in: 1.days) do | |||
| Issue.count | |||
| end | |||
| @comment_count = Rails.cache.fetch("dashboardscontroller:platform:comment_count", expires_in: 1.days) do | |||
| Journal.count | |||
| end | |||
| @pr_count = Rails.cache.fetch("dashboardscontroller:platform:pr_count", expires_in: 1.days) do | |||
| PullRequest.count | |||
| end | |||
| @commit_count = Rails.cache.fetch("dashboardscontroller:platform:commit_count", expires_in: 1.days) do | |||
| CommitLog.count | |||
| end | |||
| @subject_name = ["用户数", "项目数", "组织数", "Issue数", "Issue评论数", "PR数", "Commit数"] | |||
| @subject_icon = ["fa-user","fa-git", "fa-sitemap", "fa-warning", "fa-comments", "fa-share-alt", "fa-upload"] | |||
| @subject_data = [@user_count, @project_count, @organization_count, @issue_count, @comment_count, @pr_count, @commit_count] | |||
| tongji_service = Baidu::TongjiService.new | |||
| @access_token = tongji_service.access_token | |||
| Rails.logger.info "baidu_tongji_auth access_token ===== #{@access_token}" | |||
| # @overview_data = tongji_service.api_overview | |||
| last_date = DailyPlatformStatistic.order(:date).last | |||
| start_date = last_date.date | |||
| end_date = Time.now | |||
| if @access_token.present? | |||
| @overview_data = Rails.cache.fetch("dashboardscontroller:baidu_tongji:overview_data", expires_in: 10.minutes) do | |||
| tongji_service.source_from_batch_add(start_date, end_date) | |||
| @overview_data = tongji_service.overview_batch_add(start_date, end_date) | |||
| @overview_data | |||
| end | |||
| end | |||
| @current_week_statistic = DailyPlatformStatistic.where(date: current_week) | |||
| @pre_week_statistic = DailyPlatformStatistic.where(date: pre_week) | |||
| end | |||
| def month_active_user | |||
| @@ -42,6 +104,19 @@ class Admins::DashboardsController < Admins::BaseController | |||
| render_ok(data: data) | |||
| end | |||
| def baidu_tongji | |||
| tongji_service = Baidu::TongjiService.new | |||
| redirect_to tongji_service.code_url | |||
| end | |||
| def baidu_tongji_auth | |||
| if params[:code].present? | |||
| tongji_service = Baidu::TongjiService.new | |||
| tongji_service.get_access_token(params[:code]) | |||
| end | |||
| redirect_to "/admins/" | |||
| end | |||
| def evaluate | |||
| names = [] | |||
| data = [] | |||
| @@ -63,8 +138,12 @@ class Admins::DashboardsController < Admins::BaseController | |||
| Time.now.beginning_of_day..Time.now.end_of_day | |||
| end | |||
| def current_week | |||
| def pre_7_days | |||
| 7.days.ago.end_of_day..Time.now.end_of_day | |||
| end | |||
| def current_week | |||
| Time.now.beginning_of_week..Time.now.end_of_day | |||
| end | |||
| def current_month | |||
| @@ -72,6 +151,7 @@ class Admins::DashboardsController < Admins::BaseController | |||
| end | |||
| def pre_week | |||
| 14.days.ago.end_of_day..7.days.ago.end_of_day | |||
| # 14.days.ago.end_of_day..7.days.ago.end_of_day | |||
| Time.now.prev_week..Time.now.prev_week.end_of_week | |||
| end | |||
| end | |||
| @@ -14,12 +14,14 @@ class Admins::IdentityVerificationsController < Admins::BaseController | |||
| end | |||
| def update | |||
| if @identity_verification.update(update_params) | |||
| if update_params[:state] == "已拒绝" && update_params[:description].blank? | |||
| flash[:danger] = '拒绝理由不能为空' | |||
| render 'edit' | |||
| else | |||
| UserAction.create(action_id: @identity_verification.id, action_type: "UpdateIdentityVerifications", user_id: current_user.id, :ip => request.remote_ip, data_bank: @identity_verification.attributes.to_json) | |||
| @identity_verification.update(update_params) | |||
| redirect_to admins_identity_verifications_path | |||
| flash[:success] = "更新成功" | |||
| else | |||
| redirect_to admins_identity_verifications_path | |||
| flash[:danger] = "更新失败" | |||
| end | |||
| end | |||
| @@ -0,0 +1,29 @@ | |||
| class Admins::IssuesRankController < Admins::BaseController | |||
| def index | |||
| @statistics = DailyProjectStatistic.where('date >= ? AND date <= ?', begin_date, end_date) | |||
| @statistics = @statistics.group(:project_id).joins(:project).select("project_id, | |||
| sum(issues) as issues, | |||
| sum(closed_issues) as closed_issues, | |||
| projects.issues_count as issues_count") | |||
| @statistics = @statistics.order("#{sort_by} #{sort_direction}").limit(50) | |||
| end | |||
| private | |||
| def begin_date | |||
| params.fetch(:begin_date, (Date.yesterday-7.days).to_s) | |||
| end | |||
| def end_date | |||
| params.fetch(:end_date, Date.yesterday.to_s) | |||
| end | |||
| def sort_by | |||
| DailyProjectStatistic.column_names.include?(params.fetch(:sort_by, "issues")) ? params.fetch(:sort_by, "issues") : "issues" | |||
| end | |||
| def sort_direction | |||
| %w(desc asc).include?(params.fetch(:sort_direction, "desc")) ? params.fetch(:sort_direction, "desc") : "desc" | |||
| end | |||
| end | |||
| @@ -1,16 +1,55 @@ | |||
| class Admins::ProjectsRankController < Admins::BaseController | |||
| def index | |||
| @rank_date = rank_date | |||
| deleted_data = $redis_cache.smembers("v2-project-rank-deleted") | |||
| $redis_cache.zrem("v2-project-rank-#{rank_date}", deleted_data) unless deleted_data.blank? | |||
| @date_rank = $redis_cache.zrevrange("v2-project-rank-#{rank_date}", 0, -1, withscores: true) | |||
| @statistics = DailyProjectStatistic.where("date >= ? AND date <= ?", begin_date, end_date) | |||
| @statistics = @statistics.group(:project_id).select("project_id, | |||
| sum(score) as score, | |||
| sum(visits) as visits, | |||
| sum(watchers) as watchers, | |||
| sum(praises) as praises, | |||
| sum(forks) as forks, | |||
| sum(issues) as issues, | |||
| sum(pullrequests) as pullrequests, | |||
| sum(commits) as commits").includes(:project) | |||
| @statistics = @statistics.order("#{sort_by} #{sort_direction}") | |||
| export_excel(@statistics.limit(50)) | |||
| end | |||
| private | |||
| def rank_date | |||
| params.fetch(:date, Date.today.to_s) | |||
| def begin_date | |||
| params.fetch(:begin_date, (Date.yesterday-7.days).to_s) | |||
| end | |||
| def end_date | |||
| params.fetch(:end_date, Date.yesterday.to_s) | |||
| end | |||
| def sort_by | |||
| DailyProjectStatistic.column_names.include?(params.fetch(:sort_by, "score")) ? params.fetch(:sort_by, "score") : "score" | |||
| end | |||
| def sort_direction | |||
| %w(desc asc).include?(params.fetch(:sort_direction, "desc")) ? params.fetch(:sort_direction, "desc") : "desc" | |||
| end | |||
| def export_excel(data) | |||
| book = Spreadsheet::Workbook.new | |||
| sheet = book.create_worksheet :name => "项目活跃度排行" | |||
| sheet.row(0).concat %w(排名 项目全称 项目地址 得分 访问数 关注数 点赞数 fork数 疑修数 合并请求数 提交数) | |||
| data.each_with_index do |d, index| | |||
| sheet[index+1,0] = index+1 | |||
| sheet[index+1,1] = "#{d&.project&.owner&.real_name}/#{d&.project&.name}" | |||
| sheet[index+1,2] = "#{Rails.application.config_for(:configuration)['platform_url']}/#{d&.project&.owner&.login}/#{d&.project&.identifier}" | |||
| sheet[index+1,3] = d.score | |||
| sheet[index+1,4] = d.visits | |||
| sheet[index+1,5] = d.watchers | |||
| sheet[index+1,6] = d.praises | |||
| sheet[index+1,7] = d.forks | |||
| sheet[index+1,8] = d.issues | |||
| sheet[index+1,9] = d.pullrequests | |||
| sheet[index+1,10] = d.commits | |||
| end | |||
| book.write "#{Rails.root}/public/项目活跃度排行.xls" | |||
| end | |||
| end | |||
| @@ -29,8 +29,12 @@ class Admins::SitePagesController < Admins::BaseController | |||
| end | |||
| def update | |||
| @site_page.update(update_params) | |||
| flash[:success] = '保存成功' | |||
| if update_params[:state] == "false" && update_params[:state_description].blank? | |||
| flash[:danger] = '关闭站点理由不能为空' | |||
| else | |||
| @site_page.update(update_params) | |||
| flash[:success] = '保存成功' | |||
| end | |||
| render 'edit' | |||
| end | |||
| @@ -25,7 +25,7 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||
| @notification = SystemNotification.new(notification_params) | |||
| if @notification.save | |||
| redirect_to admins_system_notifications_path | |||
| flash[:success] = '系统消息创建成功' | |||
| flash[:success] = '系统公告创建成功' | |||
| else | |||
| redirect_to admins_system_notifications_path | |||
| flash[:danger] = @notification.errors.full_messages.join(",") | |||
| @@ -37,7 +37,7 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||
| if @notification.update_attributes(notification_params) | |||
| format.html do | |||
| redirect_to admins_system_notifications_path | |||
| flash[:success] = '系统消息更新成功' | |||
| flash[:success] = '系统公告更新成功' | |||
| end | |||
| format.js {render_ok} | |||
| else | |||
| @@ -53,10 +53,10 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||
| def destroy | |||
| if @notification.destroy | |||
| redirect_to admins_system_notifications_path | |||
| flash[:success] = "系统消息删除成功" | |||
| flash[:success] = "系统公告删除成功" | |||
| else | |||
| redirect_to admins_system_notifications_path | |||
| flash[:danger] = "系统消息删除失败" | |||
| flash[:danger] = "系统公告删除失败" | |||
| end | |||
| end | |||
| @@ -7,4 +7,12 @@ class Api::V1::Issues::IssuePrioritiesController < Api::V1::BaseController | |||
| @priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword] | |||
| @priorities = kaminary_select_paginate(@priorities) | |||
| end | |||
| def pm_index | |||
| @priorities = IssuePriority.order(position: :asc) | |||
| @priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword] | |||
| @priorities = kaminary_select_paginate(@priorities) | |||
| render "index" | |||
| end | |||
| end | |||
| @@ -1,5 +1,5 @@ | |||
| class Api::V1::Issues::IssueTagsController < Api::V1::BaseController | |||
| before_action :require_login, except: [:index] | |||
| before_action :require_login, except: [:index, :pm_index] | |||
| before_action :require_public_and_member_above, only: [:index] | |||
| before_action :require_operate_above, only: [:create, :update, :destroy] | |||
| @@ -7,12 +7,17 @@ class Api::V1::Issues::IssueTagsController < Api::V1::BaseController | |||
| @issue_tags = @project.issue_tags.reorder("#{sort_by} #{sort_direction}") | |||
| @issue_tags = @issue_tags.ransack(name_cont: params[:keyword]).result if params[:keyword].present? | |||
| if params[:only_name] | |||
| @issue_tags = kaminary_select_paginate(@issue_tags.select(:id, :name, :color)) | |||
| @issue_tags = kaminary_select_paginate(@issue_tags.select(:id, :name, :color)) | |||
| else | |||
| @issue_tags = kaminari_paginate(@issue_tags.includes(:project, :user, :issue_issues, :pull_request_issues)) | |||
| end | |||
| end | |||
| def pm_index | |||
| @issue_tags = IssueTag.init_mp_issues_tags | |||
| render_ok(@issue_tags) | |||
| end | |||
| def create | |||
| @issue_tag = @project.issue_tags.new(issue_tag_params) | |||
| if @issue_tag.save! | |||
| @@ -8,4 +8,11 @@ class Api::V1::Issues::StatuesController < Api::V1::BaseController | |||
| @statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present? | |||
| @statues = kaminary_select_paginate(@statues) | |||
| end | |||
| def pm_index | |||
| @statues = IssueStatus.order("position asc") | |||
| @statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present? | |||
| @statues = kaminary_select_paginate(@statues) | |||
| render "index" | |||
| end | |||
| end | |||
| @@ -1,6 +1,6 @@ | |||
| class Api::V1::IssuesController < Api::V1::BaseController | |||
| before_action :require_login, except: [:index, :show] | |||
| before_action :require_public_and_member_above, only: [:index, :show, :create, :update, :destroy] | |||
| before_action :require_login, except: [:index, :show, :show_by_id] | |||
| before_action :require_public_and_member_above, only: [:index, :show, :show_by_id, :create, :update, :destroy] | |||
| before_action :require_operate_above, only: [:batch_update, :batch_destroy] | |||
| def index | |||
| @@ -22,6 +22,12 @@ class Api::V1::IssuesController < Api::V1::BaseController | |||
| before_action :load_issue, only: [:show, :update, :destroy] | |||
| before_action :check_issue_operate_permission, only: [:update, :destroy] | |||
| before_action :load_issue_by_id, only: [:show_by_id] | |||
| def show_by_id | |||
| @issue.associate_attachment_container | |||
| @user_permission = current_user.present? && current_user.logged? && (@project.member?(current_user) || current_user.admin? || @issue.user == current_user) | |||
| end | |||
| def show | |||
| @issue.associate_attachment_container | |||
| @@ -70,6 +76,13 @@ class Api::V1::IssuesController < Api::V1::BaseController | |||
| end | |||
| end | |||
| def load_issue_by_id | |||
| @issue = Issue.find_by_id(params[:index]) | |||
| if @issue.blank? | |||
| render_not_found("疑修不存在!") | |||
| end | |||
| end | |||
| def load_issues | |||
| return render_error("请输入正确的ID数组!") unless params[:ids].is_a?(Array) | |||
| params[:ids].each do |id| | |||
| @@ -0,0 +1,31 @@ | |||
| class Api::V1::Projects::Actions::ActionsController < Api::V1::Projects::Actions::BaseController | |||
| def index | |||
| begin | |||
| gitea_result = $gitea_hat_client.get_repos_actions_by_owner_repo(@project&.owner&.login, @project&.identifier) | |||
| @data = gitea_result[:data]["Workflows"] | |||
| rescue | |||
| @data = [] | |||
| end | |||
| end | |||
| def disable | |||
| return render_error("请输入正确的流水线文件!") if params[:workflow].blank? | |||
| gitea_result = $gitea_hat_client.post_repos_actions_disable(@project&.owner&.login, @project&.identifier, {query: {workflow: params[:workflow]}}) rescue nil | |||
| if gitea_result | |||
| render_ok | |||
| else | |||
| render_error("禁用流水线失败") | |||
| end | |||
| end | |||
| def enable | |||
| return render_error("请输入正确的流水线文件!") if params[:workflow].blank? | |||
| gitea_result = $gitea_hat_client.post_repos_actions_enable(@project&.owner&.login, @project&.identifier, {query: {workflow: params[:workflow]}}) rescue nil | |||
| if gitea_result | |||
| render_ok | |||
| else | |||
| render_error("取消禁用流水线失败") | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,4 @@ | |||
| class Api::V1::Projects::Actions::BaseController < Api::V1::BaseController | |||
| before_action :require_public_and_member_above | |||
| end | |||
| @@ -0,0 +1,12 @@ | |||
| class Api::V1::Projects::Actions::RunsController < Api::V1::Projects::Actions::BaseController | |||
| def index | |||
| @result_object = Api::V1::Projects::Actions::Runs::ListService.call(@project, {workflow: params[:workflow], page: page, limit: limit}, current_user&.gitea_token) | |||
| puts @result_object | |||
| end | |||
| def job_show | |||
| @result_object = Api::V1::Projects::Actions::Runs::JobShowService.call(@project, params[:run_id], params[:job], params[:log_cursors], current_user&.gitea_token) | |||
| end | |||
| end | |||
| @@ -2,14 +2,14 @@ class Api::V1::Projects::BranchesController < Api::V1::BaseController | |||
| before_action :require_public_and_member_above, only: [:index, :all] | |||
| def index | |||
| @result_object = Api::V1::Projects::Branches::ListService.call(@project, {name: params[:keyword], page: page, limit: limit}, current_user&.gitea_token) | |||
| @result_object = Api::V1::Projects::Branches::ListService.call(@project, {name: params[:keyword], state: params[:state], page: page, limit: limit}, current_user&.gitea_token) | |||
| end | |||
| def all | |||
| @result_object = Api::V1::Projects::Branches::AllListService.call(@project, current_user&.gitea_token) | |||
| end | |||
| before_action :require_operate_above, only: [:create, :destroy] | |||
| before_action :require_operate_above, only: [:create, :destroy, :restore] | |||
| def create | |||
| @result_object = Api::V1::Projects::Branches::CreateService.call(@project, branch_params, current_user&.gitea_token) | |||
| @@ -33,6 +33,15 @@ class Api::V1::Projects::BranchesController < Api::V1::BaseController | |||
| end | |||
| end | |||
| def restore | |||
| @result_object = Api::V1::Projects::Branches::RestoreService.call(@project, params[:branch_id], params[:branch_name], current_user&.gitea_token) | |||
| if @result_object | |||
| return render_ok | |||
| else | |||
| return render_error('恢复分支失败!') | |||
| end | |||
| end | |||
| before_action :require_manager_above, only: [:update_default_branch] | |||
| def update_default_branch | |||
| @@ -1,5 +1,5 @@ | |||
| class Api::V1::Projects::CommitsController < Api::V1::BaseController | |||
| before_action :require_public_and_member_above, only: [:index, :diff] | |||
| before_action :require_public_and_member_above, only: [:index, :diff, :recent] | |||
| def index | |||
| @result_object = Api::V1::Projects::Commits::ListService.call(@project, {page: page, limit: limit, sha: params[:sha]}, current_user&.gitea_token) | |||
| @@ -9,4 +9,8 @@ class Api::V1::Projects::CommitsController < Api::V1::BaseController | |||
| def diff | |||
| @result_object = Api::V1::Projects::Commits::DiffService.call(@project, params[:sha], current_user&.gitea_token) | |||
| end | |||
| def recent | |||
| @result_object = Api::V1::Projects::Commits::RecentService.call(@project, {keyword: params[:keyword], page: page, limit: limit}, current_user&.gitea_token) | |||
| end | |||
| end | |||
| @@ -1,10 +1,13 @@ | |||
| class Api::V1::Projects::TagsController < Api::V1::BaseController | |||
| before_action :require_public_and_member_above, only: [:index] | |||
| before_action :require_public_and_member_above, only: [:index, :show] | |||
| def index | |||
| @release_tags = @repository.version_releases.pluck(:tag_name) | |||
| @result_object = Api::V1::Projects::Tags::ListService.call(@project, {page: page, limit: limit}, current_user&.gitea_token) | |||
| puts @result_object | |||
| end | |||
| def show | |||
| @result_object = Api::V1::Projects::Tags::GetService.call(@project, params[:name], current_user&.gitea_token) | |||
| end | |||
| before_action :require_operate_above, only: [:destroy] | |||
| @@ -715,7 +715,7 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| def find_user_with_id | |||
| @user = User.find_by_id params[:user_id] | |||
| @user = User.find_by(type: 'User', id: params[:user_id]) | |||
| # render_not_found("未找到’#{params[:login]}’相关的用户") unless @user | |||
| render_error("未找到相关的用户") unless @user | |||
| end | |||
| @@ -94,6 +94,7 @@ class AttachmentsController < ApplicationController | |||
| @attachment.author_id = current_user.id | |||
| @attachment.disk_directory = month_folder | |||
| @attachment.cloud_url = remote_path | |||
| @attachment.uuid = SecureRandom.uuid | |||
| @attachment.save! | |||
| else | |||
| logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}" | |||
| @@ -143,12 +144,14 @@ class AttachmentsController < ApplicationController | |||
| private | |||
| def find_file | |||
| tip_exception(404, "您访问的页面不存在或已被删除") if params[:id].blank? | |||
| @file = | |||
| if params[:type] == 'history' | |||
| AttachmentHistory.find params[:id] | |||
| else | |||
| Attachment.find params[:id] | |||
| Attachment.where_id_or_uuid(params[:id]).first | |||
| end | |||
| tip_exception(404, "您访问的页面不存在或已被删除") if @file.blank? | |||
| end | |||
| def delete_file(file_path) | |||
| @@ -218,7 +221,7 @@ class AttachmentsController < ApplicationController | |||
| def attachment_candown | |||
| unless current_user.admin? || current_user.business? | |||
| candown = true | |||
| if @file.container | |||
| if @file.container && @file.uuid.nil? | |||
| if @file.container.is_a?(Issue) | |||
| project = @file.container.project | |||
| candown = project.is_public || (current_user.logged? && project.member?(current_user)) | |||
| @@ -8,7 +8,7 @@ class BindUsersController < ApplicationController | |||
| bind_user = User.try_to_login(params[:username], params[:password]) | |||
| tip_exception '用户名或者密码错误' if bind_user.blank? | |||
| tip_exception '用户名或者密码错误' unless bind_user.check_password?(params[:password].to_s) | |||
| tip_exception '参数错误' unless ["qq", "wechat", "gitee", "github", "educoder"].include?(params[:type].to_s) | |||
| tip_exception '参数错误' unless ["qq", "wechat", "gitee", "github", "educoder", "acge"].include?(params[:type].to_s) | |||
| tip_exception '该账号已被绑定,请更换其他账号进行绑定' if bind_user.bind_open_user?(params[:type].to_s) | |||
| "OpenUsers::#{params[:type].to_s.capitalize}".constantize.create!(user: bind_user, uid: session[:unionid]) | |||
| @@ -139,7 +139,7 @@ class IssuesController < ApplicationController | |||
| SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @issue&.id) if Site.has_notice_menu? | |||
| if params[:attachment_ids].present? | |||
| params[:attachment_ids].each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| attachment = Attachment.where_id_or_uuid(id).select(:id, :container_id, :container_type)&.first | |||
| unless attachment.blank? | |||
| attachment.container = @issue | |||
| attachment.author_id = current_user.id | |||
| @@ -232,7 +232,7 @@ class IssuesController < ApplicationController | |||
| if issue_files.present? | |||
| change_files = true | |||
| issue_files.each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| attachment = Attachment.where_id_or_uuid(id).select(:id, :container_id, :container_type)&.first | |||
| unless attachment.blank? | |||
| attachment.container = @issue | |||
| attachment.author_id = current_user.id | |||
| @@ -35,7 +35,7 @@ class JournalsController < ApplicationController | |||
| if journal.save | |||
| if params[:attachment_ids].present? | |||
| params[:attachment_ids].each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| attachment = Attachment.where_id_or_uuid(id).select(:id, :container_id, :container_type)&.first | |||
| unless attachment.blank? | |||
| attachment.container = journal | |||
| attachment.author_id = current_user.id | |||
| @@ -0,0 +1,67 @@ | |||
| class Oauth::AcgeController < Oauth::BaseController | |||
| include RegisterHelper | |||
| def create | |||
| begin | |||
| uid = params['uid'].to_s.strip | |||
| tip_exception("uid不能为空") if uid.blank? | |||
| redirect_uri = params['redirect_uri'].to_s.strip | |||
| tip_exception("redirect_uri不能为空") if redirect_uri.blank? | |||
| email = params['email'].to_s.strip | |||
| tip_exception("email不能为空") if email.blank? | |||
| phone = params['phone'].to_s.strip | |||
| tip_exception("phone不能为空") if phone.blank? | |||
| name = params['name'].to_s.strip | |||
| tip_exception("name不能为空") if name.blank? | |||
| open_user = OpenUsers::Acge.find_by(uid: uid) | |||
| if open_user.present? && open_user.user.present? | |||
| successful_authentication(open_user.user) | |||
| redirect_to redirect_uri | |||
| return | |||
| else | |||
| if current_user.blank? || !current_user.logged? | |||
| session[:unionid] = uid | |||
| user = User.find_by(mail: email) || User.find_by(phone: phone) | |||
| if user.present? | |||
| OpenUsers::Acge.create!(user: user, uid: uid) | |||
| successful_authentication(user) | |||
| redirect_to redirect_uri | |||
| return | |||
| else | |||
| username = uid[0..7] | |||
| password = SecureRandom.hex(4) | |||
| reg_result = autologin_register(username, email, password, 'acge', phone, name) | |||
| existing_rows = CSV.read("public/操作系统大赛用户信息.csv") | |||
| new_row = [username, email, password, phone, name] | |||
| existing_rows << new_row | |||
| CSV.open("public/操作系统大赛用户信息.csv", 'wb') do |csv| | |||
| existing_rows.each { |row| csv << row } | |||
| end | |||
| if reg_result[:message].blank? | |||
| open_user = OpenUsers::Acge.create!(user_id: reg_result[:user][:id], uid: uid) | |||
| successful_authentication(open_user.user) | |||
| redirect_to redirect_uri | |||
| return | |||
| else | |||
| render_error(reg_result[:message]) | |||
| end | |||
| end | |||
| else | |||
| OpenUsers::Acge.create!(user: current_user, uid: uid) | |||
| successful_authentication(current_user) | |||
| redirect_to redirect_uri | |||
| return | |||
| end | |||
| end | |||
| Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}") | |||
| # redirect_to "/bindlogin/acge?redirect_uri=#{redirect_uri}" | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| end | |||
| end | |||
| @@ -20,12 +20,12 @@ class Oauth2Controller < ActionController::Base | |||
| return @error = {msg: '违反平台使用规范,账号已被锁定', id: 'login'} if @user.locked? | |||
| login_control = LimitForbidControl::UserLogin.new(@user) | |||
| return @error = {msg: "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'} if login_control.forbid? | |||
| return @error = {msg: "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'} if login_control.forbid? | |||
| password_ok = @user.check_password?(params[:password].to_s) | |||
| unless password_ok | |||
| if login_control.remain_times-1 == 0 | |||
| @error = {msg: "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'} | |||
| @error = {msg: "登录密码出错已达上限,账号已被锁定,请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'} | |||
| else | |||
| @error = {msg: "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会", id: 'account'} | |||
| end | |||
| @@ -4,17 +4,31 @@ class Organizations::OrganizationUsersController < Organizations::BaseController | |||
| before_action :check_user_can_edit_org, only: [:destroy] | |||
| def index | |||
| @organization_users = @organization.organization_users.includes(:user) | |||
| # @organization_users = @organization.organization_users.includes(:user) | |||
| # if params[:search].present? | |||
| # search = params[:search].to_s.downcase | |||
| # user_condition_users = User.like(search).to_sql | |||
| # team_condition_teams = User.joins(:teams).merge(@organization.teams.like(search)).to_sql | |||
| # users = User.from("( #{user_condition_users} UNION #{team_condition_teams }) AS users") | |||
| # | |||
| # @organization_users = @organization_users.where(user_id: users).distinct | |||
| # end | |||
| # | |||
| # @organization_users = kaminari_paginate(@organization_users) | |||
| organization_user_ids = @organization.organization_users.pluck(:user_id).uniq | |||
| project_member_user_ids = @organization.projects.joins(:members).pluck("members.user_id").uniq | |||
| ids = organization_user_ids + project_member_user_ids | |||
| users = User.where(id: ids).reorder(Arel.sql("FIELD(users.id,#{ids.join(',')})")) | |||
| if params[:search].present? | |||
| search = params[:search].to_s.downcase | |||
| user_condition_users = User.like(search).to_sql | |||
| team_condition_teams = User.joins(:teams).merge(@organization.teams.like(search)).to_sql | |||
| users = User.from("( #{user_condition_users} UNION #{team_condition_teams }) AS users") | |||
| user_ids = User.from("( #{user_condition_users} UNION #{team_condition_teams }) AS users").pluck(:id) | |||
| @organization_users = @organization_users.where(user_id: users).distinct | |||
| users = users.where(id: user_ids) | |||
| end | |||
| @organization_users = kaminari_paginate(@organization_users) | |||
| @users = kaminari_paginate(users) | |||
| end | |||
| def pm_check_user | |||
| @@ -4,9 +4,9 @@ class ProjectsController < ApplicationController | |||
| include ProjectsHelper | |||
| include Acceleratorable | |||
| before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list verify_auth_token] | |||
| before_action :require_login, except: %i[index branches branches_slice group_type_list simple show mp_show fork_users praise_users watch_users recommend banner_recommend about menu_list verify_auth_token] | |||
| before_action :require_profile_completed, only: [:create, :migrate,:page_migrate,:verify_auth_token] | |||
| before_action :load_repository, except: %i[index group_type_list migrate page_migrate create recommend banner_recommend verify_auth_token] | |||
| before_action :load_repository, except: %i[index mp_show group_type_list migrate page_migrate create recommend banner_recommend verify_auth_token] | |||
| before_action :authorizate_user_can_edit_project!, only: %i[update] | |||
| before_action :project_public?, only: %i[fork_users praise_users watch_users] | |||
| before_action :request_limit, only: %i[index] | |||
| @@ -193,13 +193,19 @@ class ProjectsController < ApplicationController | |||
| default_branch: @project.default_branch | |||
| } | |||
| Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) | |||
| elsif project_params.has_key?("has_actions") | |||
| gitea_params = { | |||
| has_actions: project_params[:has_actions] | |||
| } | |||
| Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) | |||
| else | |||
| validate_params = project_params.slice(:name, :description, | |||
| :project_category_id, :project_language_id, :private, :identifier) | |||
| Projects::UpdateForm.new(validate_params.merge(user_id: @project.user_id, project_identifier: @project.identifier, project_name: @project.name)).validate! | |||
| private = @project.forked_from_project.present? ? !@project.forked_from_project.is_public : params[:private] || false | |||
| private = params[:private].nil? ? !@project.is_public : params[:private] | |||
| private = @project.forked_from_project.present? ? !@project.forked_from_project.is_public : private | |||
| new_project_params = project_params.except(:private).merge(is_public: !private) | |||
| @project.update_attributes!(new_project_params) | |||
| @@ -232,6 +238,15 @@ class ProjectsController < ApplicationController | |||
| def show | |||
| end | |||
| def mp_show | |||
| @project = Project.joins(:owner).find params[:project_id] | |||
| data={ | |||
| owner:@project.owner.try(:login), | |||
| identifier:@project.identifier | |||
| } | |||
| render_ok(data:data) | |||
| end | |||
| def destroy | |||
| if current_user.admin? || @project.manager?(current_user) | |||
| ActiveRecord::Base.transaction do | |||
| @@ -338,7 +353,7 @@ class ProjectsController < ApplicationController | |||
| def project_params | |||
| params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, :default_branch, :identifier, | |||
| :project_category_id, :project_language_id, :license_id, :ignore_id, :private, | |||
| :project_category_id, :project_language_id, :license_id, :ignore_id, :private, :has_actions, | |||
| :blockchain, :blockchain_token_all, :blockchain_init_token, :pr_view_admin) | |||
| end | |||
| @@ -203,6 +203,7 @@ class PullRequestsController < ApplicationController | |||
| def pr_merge | |||
| return render_forbidden("你没有权限操作.") unless @project.operator?(current_user) | |||
| return normal_status(-1, "该分支存在冲突,无法自动合并.") unless @pull_request.conflict_files.blank? | |||
| if params[:do].blank? | |||
| normal_status(-1, "请选择合并方式") | |||
| @@ -66,6 +66,8 @@ class RepositoriesController < ApplicationController | |||
| @entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call | |||
| @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : [] | |||
| @path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" | |||
| @repo_detail = $gitea_client.get_repos_by_owner_repo(@owner.login, @project.identifier) | |||
| return render_not_found if @entries.blank? && !@repo_detail["empty"] | |||
| end | |||
| end | |||
| @@ -17,23 +17,31 @@ class SitePagesController < ApplicationController | |||
| end | |||
| def create | |||
| return normal_status(-1, "你还未开通Page服务,无法进行部署") unless current_user.website_permission | |||
| return normal_status(-1, "你已使用了 #{params[:identifier]} 作为page标识") if Page.exists?(identifier: params[:identifier], user: current_user) | |||
| return normal_status(-1, "该仓库已开通Page服务") if Page.exists?(project: @project) | |||
| return normal_status(-1, '你还未开通Page服务,无法进行部署') unless current_user.website_permission | |||
| return normal_status(-1, '你已开通Page服务') if Page.exists?(user: current_user) | |||
| return normal_status(-1, '该仓库已开通Page服务') if Page.exists?(project: @project) | |||
| @page = Page.new(create_params) | |||
| @page.user = current_user | |||
| @page.project = @project | |||
| @page.save | |||
| end | |||
| def update | |||
| return normal_status(-1, '你还未开通Page服务') unless current_user.website_permission | |||
| return normal_status(-1, '你还未开通Page站点') unless Page.exists?(user: current_user) | |||
| @page = Page.find_by(user: current_user) | |||
| @page.update(language_frame: params[:language_frame]) | |||
| render_ok | |||
| end | |||
| def build | |||
| return normal_status(-1, "你还未开通Page服务,无法进行部署") unless current_user.website_permission | |||
| return normal_status(-1, "该仓库还未开通Page服务,无法进行部署") unless Page.exists?(project: @project) | |||
| return normal_status(-1, '你还未开通Page服务,无法进行部署') unless current_user.website_permission | |||
| return normal_status(-1, '该仓库还未开通Page服务,无法进行部署') unless Page.exists?(project: @project) | |||
| @page = Page.find params[:id] | |||
| return normal_status(-1, @page.state_description) unless @page.state | |||
| response_str = @page.deploy_page(params[:branch]) | |||
| data = JSON.parse(response_str)["result"] || data = JSON.parse(response_str)["error"] | |||
| if data.to_s.include?("部署成功") | |||
| data = JSON.parse(response_str)['result'] || (data = JSON.parse(response_str)['error']) | |||
| if data.to_s.include?('部署成功') | |||
| @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) | |||
| else | |||
| @page.update(build_state:false, last_build_info: data) | |||
| @@ -42,22 +50,22 @@ class SitePagesController < ApplicationController | |||
| end | |||
| def softbot_build | |||
| branch = params[:ref].split("/").last | |||
| branch = params[:ref].split('/').last | |||
| user = User.find_by_login params[:repository][:owner][:login] | |||
| return normal_status(-1, "你还未开通Page服务,无法进行部署") unless user.website_permission | |||
| return normal_status(-1, '你还未开通Page服务,无法进行部署') unless user.website_permission | |||
| project = Project.where(identifier: params[:repository][:name],user_id: user.id) | |||
| return normal_status(-1, "你没有权限操作") if project.owner?(user) | |||
| return normal_status(-1, "该仓库还未开通Page服务,无法进行部署") if Page.exists?(user: user, project: project) | |||
| return normal_status(-1, '你没有权限操作') if project.owner?(user) | |||
| return normal_status(-1, '该仓库还未开通Page服务,无法进行部署') if Page.exists?(user: user, project: project) | |||
| @page = Page.find_by(user: user, project: project) | |||
| response_str = @page.deploy_page(branch) | |||
| data = JSON.parse(response_str)["result"] | |||
| data = JSON.parse(response_str)['result'] | |||
| if data.nil? | |||
| data = JSON.parse(response_str)["error"] | |||
| data = JSON.parse(response_str)['error'] | |||
| end | |||
| if data.include?("部署成功") | |||
| if data.include?('部署成功') | |||
| @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) | |||
| else | |||
| @page.update(build_state:false, last_build_info: data) | |||
| @@ -85,7 +93,7 @@ class SitePagesController < ApplicationController | |||
| end | |||
| def theme_params | |||
| params[:language_frame] || "hugo" | |||
| params[:language_frame] || 'hugo' | |||
| end | |||
| def create_params | |||
| @@ -22,7 +22,7 @@ class UsersController < ApplicationController | |||
| end | |||
| def list | |||
| scope = User.active.recent.like(params[:search]).includes(:user_extension) | |||
| scope = User.active.like(params[:search]).includes(:user_extension).order(nickname: :desc, last_login_on: :desc) | |||
| @total_count = scope.size | |||
| @users = paginate(scope) | |||
| end | |||
| @@ -152,11 +152,12 @@ class VersionReleasesController < ApplicationController | |||
| def create_attachments(attachment_ids, target) | |||
| attachment_ids.each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| attachment = Attachment.where_id_or_uuid(id).select(:id, :container_id, :container_type)&.first | |||
| unless attachment.blank? | |||
| attachment.container = target | |||
| attachment.author_id = current_user.id | |||
| attachment.description = "" | |||
| attachment.uuid = SecureRandom.uuid | |||
| attachment.save | |||
| end | |||
| end | |||
| @@ -1,12 +1,12 @@ | |||
| class Organizations::CreateForm < BaseForm | |||
| NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| NAME_REGEX = /^[a-zA-Z0-9]+([-_.][a-zA-Z0-9]+)*$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| attr_accessor :name, :description, :website, :location, :repo_admin_change_team_access, :visibility, :max_repo_creation, :nickname, :original_name | |||
| validates :name, :nickname, :visibility, presence: true | |||
| validates :name, :nickname, length: { maximum: 100 } | |||
| validates :location, length: { maximum: 50 } | |||
| validates :description, length: { maximum: 200 } | |||
| validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能以数字或字母开头,仅支持横杠、下划线、点三种符号,不允许符号连续排列,长度4-50个字符" } | |||
| validate do | |||
| check_name(name) unless name.blank? || name == original_name | |||
| @@ -28,6 +28,10 @@ class Projects::CreateForm < BaseForm | |||
| raise "ignore_id值无效." if ignore_id && Ignore.find_by(id: ignore_id).blank? | |||
| end | |||
| def check_auto_init | |||
| raise "auto_init值无效." if ignore_id && license_id && !auto_init | |||
| end | |||
| def check_owner | |||
| @project_owner = Owner.find_by(id: user_id) | |||
| raise "user_id值无效." if user_id && @project_owner.blank? | |||
| @@ -299,7 +299,7 @@ module ApplicationHelper | |||
| end | |||
| def download_url attachment,options={} | |||
| attachment_path(attachment,options) | |||
| attachment&.uuid.present? ? attachment_path(attachment.uuid,options) : attachment_path(attachment,options) | |||
| end | |||
| # 耗时:天、小时、分、秒 | |||
| @@ -132,6 +132,8 @@ module RepositoriesHelper | |||
| src_regex_3 = /src= (.*?) / | |||
| src_regex_4 = /src =(.*?) / | |||
| src_regex_5 = /src =(.*?) / | |||
| href_regex = /href=\"(.*?)\"/ | |||
| href_regex_1 = /href=\'(.*?)\'/ | |||
| ss_c = content.to_s.scan(s_regex_c) | |||
| ss = content.to_s.scan(s_regex) | |||
| ss_1 = content.to_s.scan(s_regex_1) | |||
| @@ -142,7 +144,9 @@ module RepositoriesHelper | |||
| ss_src_3 = content.to_s.scan(src_regex_3) | |||
| ss_src_4 = content.to_s.scan(src_regex_4) | |||
| ss_src_5 = content.to_s.scan(src_regex_5) | |||
| total_sources = {ss_c: ss_c,ss: ss, ss_1: ss_1, ss_2: ss_2, ss_src: ss_src, ss_src_1: ss_src_1, ss_src_2: ss_src_2, ss_src_3: ss_src_3, ss_src_4: ss_src_4, ss_src_5: ss_src_5} | |||
| ss_href = content.to_s.scan(href_regex) | |||
| ss_href_1 = content.to_s.scan(href_regex_1) | |||
| total_sources = {ss_c: ss_c,ss: ss, ss_1: ss_1, ss_2: ss_2, ss_src: ss_src, ss_src_1: ss_src_1, ss_src_2: ss_src_2, ss_src_3: ss_src_3, ss_src_4: ss_src_4, ss_src_5: ss_src_5, ss_href: ss_href, ss_href_1: ss_href_1} | |||
| # total_sources.uniq! | |||
| total_sources.except(:ss, :ss_c).each do |k, sources| | |||
| sources.each do |s| | |||
| @@ -173,13 +177,17 @@ module RepositoriesHelper | |||
| content = content.gsub("src=#{s[0]}", "src=\'#{s_content}\'") | |||
| when 'ss_2' | |||
| content = content.gsub(/]:#{s[0]}/, "]: #{s_content.to_s.gsub(" ","").gsub("\r", "")}") | |||
| else | |||
| when 'ss_href' | |||
| content = content.gsub("href=\"#{s[0]}\"", "href=\"#{s_content}\"") | |||
| when 'ss_href_1' | |||
| content = content.gsub("href=\'#{s[0]}\'", "href=\'#{s_content}\'") | |||
| else | |||
| content = content.gsub("(#{s[0]})", "(#{s_content})") | |||
| end | |||
| else | |||
| path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/") | |||
| s_content = File.expand_path(s_content, path) | |||
| s_content = s_content.split("#{Rails.root}/")[1] | |||
| s_content = s_content.split("#{Rails.root}")[1] | |||
| case k.to_s | |||
| when 'ss_src' | |||
| content = content.gsub("src=\"#{s[0]}\"", "src=\"/#{s_content}\"") | |||
| @@ -187,7 +195,11 @@ module RepositoriesHelper | |||
| content = content.gsub("src=\'#{s[0]}\'", "src=\'/#{s_content}\'") | |||
| when 'ss_2' | |||
| content = content.gsub(/]:#{s[0]}/, "]: /#{s_content.to_s.gsub(" ","").gsub("\r", "")}") | |||
| else | |||
| when 'ss_href' | |||
| content = content.gsub("href=\"#{s[0]}\"", "href=\"#{s_content}\"") | |||
| when 'ss_href_1' | |||
| content = content.gsub("href=\'#{s[0]}\'", "href=\'#{s_content}\'") | |||
| else | |||
| content = content.gsub("(#{s[0]})", "(/#{s_content})") | |||
| end | |||
| end | |||
| @@ -45,6 +45,7 @@ module Gitea | |||
| else | |||
| Rails.logger.error("Gitea::Repository::Entries::DeleteService error[#{response.status}]======#{response.body}") | |||
| @error = "删除失败,请确认该分支是否是保护分支。" | |||
| @error = "删除失败,参数sha不匹配。" if response.body.to_s.include?("sha does not match") | |||
| end | |||
| end | |||
| @@ -0,0 +1,44 @@ | |||
| # 按天获取百度统计数据,pv,访问,ip和来源分类占比 | |||
| # 其他统计:前一周用户留存率 | |||
| class DailyPlatformStatisticsJob < ApplicationJob | |||
| queue_as :default | |||
| def perform(*args) | |||
| Rails.logger.info("*********开始统计*********") | |||
| tongji_service = Baidu::TongjiService.new | |||
| access_token = tongji_service.access_token | |||
| Rails.logger.info "job baidu_tongji_auth access_token ===== #{access_token}" | |||
| ActiveJob::Base.logger.info "job baidu_tongji_auth access_token ===== #{access_token}" | |||
| # 从最后一个记录日期开始,如果遗漏日期数据可以补充数据 | |||
| last_date = DailyPlatformStatistic.order(:date).last | |||
| start_date = last_date.date | |||
| end_date = Time.now | |||
| if access_token.present? | |||
| tongji_service.overview_batch_add(start_date, end_date) | |||
| # 本周访问来源占比,每天记录一次,如果遗漏日期数据可以补充数据 | |||
| tongji_service.source_from_batch_add(start_date, end_date) | |||
| end | |||
| # 周用户留存率 | |||
| pre_week_user_ids = User.where(created_on: pre_week).pluck(:id).uniq | |||
| weekly_keep_user_count = User.where(id: pre_week_user_ids).where(last_login_on: current_week).count | |||
| weekly_keep_rate = format("%.2f", pre_week_user_ids.size > 0 ? weekly_keep_user_count.to_f / pre_week_user_ids.size : 0) | |||
| job_date = 1.days.ago | |||
| daily_statistic = DailyPlatformStatistic.find_or_initialize_by(date: job_date) | |||
| daily_statistic.weekly_keep_rate = weekly_keep_rate | |||
| daily_statistic.save | |||
| end | |||
| private | |||
| def current_week | |||
| Time.now.beginning_of_week..Time.now.end_of_day | |||
| end | |||
| def pre_week | |||
| # 7.days.ago.beginning_of_week..7.days.ago.beginning_of_week.end_of_week | |||
| Time.now.prev_week..Time.now.prev_week.end_of_week | |||
| end | |||
| end | |||
| @@ -0,0 +1,35 @@ | |||
| class DailyProjectStatisticsJob < ApplicationJob | |||
| queue_as :cache | |||
| def perform | |||
| date = (Date.today - 1.days).to_s | |||
| daily_data_keys = $redis_cache.keys("v2-project-statistic:*-#{date}") | |||
| daily_data_keys.each do |key| | |||
| result = $redis_cache.hgetall(key) | |||
| project_id = key.gsub('v2-project-statistic:', '').gsub("-#{date}", '') | |||
| next unless Project.find_by_id(project_id).present? | |||
| visits = result["visits"].to_i | |||
| watchers = result["watchers"].to_i | |||
| praises = result["praises"].to_i | |||
| forks = result["forks"].to_i | |||
| issues = result["issues"].to_i | |||
| closed_issues = result["closed_issues"].to_i | |||
| pullrequests = result["pullrequests"].to_i | |||
| commits = result["commits"].to_i | |||
| score = visits *1 + watchers *5 + praises * 5 + forks * 10 + issues *5 + pullrequests * 10 + commits * 5 | |||
| DailyProjectStatistic.create!( | |||
| project_id: project_id, | |||
| date: date, | |||
| score: score , | |||
| visits: visits, | |||
| watchers: watchers, | |||
| praises: praises, | |||
| forks: forks, | |||
| issues: issues, | |||
| closed_issues: closed_issues, | |||
| pullrequests: pullrequests, | |||
| commits: commits | |||
| ) | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,16 @@ | |||
| class DelayExpiredIssueAndMilestoneJob < ApplicationJob | |||
| queue_as :message | |||
| def perform | |||
| Issue.where(due_date: Date.today + 1.days).find_each do |issue| | |||
| SendTemplateMessageJob.perform_later('IssueExpire', issue.id) if Site.has_notice_menu? | |||
| end | |||
| Version.where(effective_date: Date.today + 1.days).find_each do |version| | |||
| SendTemplateMessageJob.perform_later('ProjectMilestoneEarlyExpired', version.id) if Site.has_notice_menu? | |||
| end | |||
| Version.where(effective_date: Date.today - 1.days).find_each do |version| | |||
| SendTemplateMessageJob.perform_later('ProjectMilestoneExpired', version.id) if Site.has_notice_menu? | |||
| end | |||
| end | |||
| end | |||
| @@ -1,10 +0,0 @@ | |||
| class DelayExpiredIssueJob < ApplicationJob | |||
| queue_as :message | |||
| def perform | |||
| Issue.where(due_date: Date.today + 1.days).find_each do |issue| | |||
| SendTemplateMessageJob.perform_later('IssueExpire', issue.id) if Site.has_notice_menu? | |||
| end | |||
| end | |||
| end | |||
| @@ -221,6 +221,20 @@ class SendTemplateMessageJob < ApplicationJob | |||
| receivers_email_string, email_title, email_content = MessageTemplate::ProjectMilestone.get_email_message_content(receiver, operator, milestone) | |||
| Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) | |||
| end | |||
| when 'ProjectMilestoneExpired' | |||
| milestone_id = args[0] | |||
| milestone = Version.find_by_id(milestone_id) | |||
| return unless milestone.present? && milestone&.project.present? | |||
| receivers = User.where(id: milestone.user_id) | |||
| receivers_string, content, notification_url = MessageTemplate::ProjectMilestoneExpired.get_message_content(receivers, milestone) | |||
| Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {milestone_id: milestone_id, operator_id: operator_id}) | |||
| when 'ProjectMilestoneEarlyExpired' | |||
| milestone_id = args[0] | |||
| milestone = Version.find_by_id(milestone_id) | |||
| return unless milestone.present? && milestone&.project.present? | |||
| receivers = User.where(id: milestone.user_id) | |||
| receivers_string, content, notification_url = MessageTemplate::ProjectMilestoneEarlyExpired.get_message_content(receivers, milestone) | |||
| Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {milestone_id: milestone_id, operator_id: operator_id}) | |||
| when 'ProjectPraised' | |||
| operator_id, project_id = args[0], args[1] | |||
| operator = User.find_by_id(operator_id) | |||
| @@ -28,6 +28,7 @@ | |||
| # delay_publish :boolean default("0") | |||
| # memo_image :boolean default("0") | |||
| # extra_type :integer default("0") | |||
| # uuid :string(255) | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -42,6 +43,7 @@ | |||
| class Attachment < ApplicationRecord | |||
| include BaseModel | |||
| include Publicable | |||
| @@ -68,6 +70,7 @@ class Attachment < ApplicationRecord | |||
| scope :simple_columns, -> { select(:id, :filename, :filesize, :created_on, :cloud_url, :author_id, :content_type, :container_type, :container_id) } | |||
| scope :search_by_container, -> (ids) {where(container_id: ids)} | |||
| scope :unified_setting, -> {where("unified_setting = ? ", 1)} | |||
| scope :where_id_or_uuid, -> (id) { (Float(id) rescue nil).present? ? where(id: id) : where(uuid: id) } | |||
| validates_length_of :description, maximum: 100, message: "不能超过100个字符" | |||
| @@ -97,6 +100,11 @@ class Attachment < ApplicationRecord | |||
| downloads | |||
| end | |||
| def generate_uuid | |||
| self.uuid = uuid || SecureRandom.uuid | |||
| save! | |||
| end | |||
| def quotes_count | |||
| quotes.nil? ? 0 : quotes | |||
| end | |||
| @@ -21,68 +21,69 @@ module ProjectOperable | |||
| end | |||
| def add_member!(user_id, role_name='Developer') | |||
| if self.owner.is_a?(Organization) | |||
| case role_name | |||
| when 'Manager' | |||
| # 构建相应的团队 | |||
| team = self.owner.teams.admin.take | |||
| if team.nil? | |||
| team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) | |||
| gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| end | |||
| # 设置项目在团队中的访问权限 | |||
| team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # 新增对应的团队成员 | |||
| team_user = TeamUser.build(self.user_id, user_id, team.id) | |||
| $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # 确保组织成员中有该用户 | |||
| OrganizationUser.build(self.user_id, user_id) | |||
| when 'Developer' | |||
| # 构建相应的团队 | |||
| team = self.owner.teams.write.take | |||
| if team.nil? | |||
| team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) | |||
| gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| end | |||
| # 设置项目在团队中的访问权限 | |||
| team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # 新增对应的团队成员 | |||
| team_user = TeamUser.build(self.user_id, user_id, team.id) | |||
| $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # 确保组织成员中有该用户 | |||
| OrganizationUser.build(self.user_id, user_id) | |||
| when 'Reporter' | |||
| # 构建相应的团队 | |||
| team = self.owner.teams.read.take | |||
| if team.nil? | |||
| team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) | |||
| gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| end | |||
| # 设置项目在团队中的访问权限 | |||
| team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # 新增对应的团队成员 | |||
| team_user = TeamUser.build(self.user_id, user_id, team.id) | |||
| $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # 确保组织成员中有该用户 | |||
| OrganizationUser.build(self.user_id, user_id) | |||
| end | |||
| end | |||
| member = members.create!(user_id: user_id, team_user_id: team_user&.id) | |||
| # if self.owner.is_a?(Organization) | |||
| # case role_name | |||
| # when 'Manager' | |||
| # # 构建相应的团队 | |||
| # team = self.owner.teams.admin.take | |||
| # if team.nil? | |||
| # team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) | |||
| # gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| # team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| # end | |||
| # | |||
| # # 设置项目在团队中的访问权限 | |||
| # team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| # tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # | |||
| # # 新增对应的团队成员 | |||
| # team_user = TeamUser.build(self.user_id, user_id, team.id) | |||
| # $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # | |||
| # # 确保组织成员中有该用户 | |||
| # OrganizationUser.build(self.user_id, user_id) | |||
| # when 'Developer' | |||
| # # 构建相应的团队 | |||
| # team = self.owner.teams.write.take | |||
| # if team.nil? | |||
| # team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) | |||
| # gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| # team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| # end | |||
| # | |||
| # # 设置项目在团队中的访问权限 | |||
| # team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| # tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # | |||
| # # 新增对应的团队成员 | |||
| # team_user = TeamUser.build(self.user_id, user_id, team.id) | |||
| # $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # | |||
| # # 确保组织成员中有该用户 | |||
| # OrganizationUser.build(self.user_id, user_id) | |||
| # when 'Reporter' | |||
| # # 构建相应的团队 | |||
| # team = self.owner.teams.read.take | |||
| # if team.nil? | |||
| # team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) | |||
| # gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| # team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| # end | |||
| # | |||
| # # 设置项目在团队中的访问权限 | |||
| # team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| # tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # | |||
| # # 新增对应的团队成员 | |||
| # team_user = TeamUser.build(self.user_id, user_id, team.id) | |||
| # $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # | |||
| # # 确保组织成员中有该用户 | |||
| # OrganizationUser.build(self.user_id, user_id) | |||
| # end | |||
| # end | |||
| # member = members.create!(user_id: user_id, team_user_id: team_user&.id) | |||
| member = members.create!(user_id: user_id) | |||
| set_developer_role(member, role_name) | |||
| end | |||
| @@ -116,71 +117,71 @@ module ProjectOperable | |||
| def change_member_role!(user_id, role) | |||
| member = self.member(user_id) | |||
| # 所有者为组织,并且该用户属于组织成员 | |||
| if self.owner.is_a?(Organization) && member.team_user.present? | |||
| case role&.name | |||
| when 'Manager' | |||
| # 构建相应的团队 | |||
| team = self.owner.teams.admin.take | |||
| if team.nil? | |||
| team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) | |||
| gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| end | |||
| # 设置项目在团队中的访问权限 | |||
| team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # 更改对应的团队成员 | |||
| team_user = member.team_user | |||
| $gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的 | |||
| $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id) | |||
| # 确保组织成员中有该用户 | |||
| OrganizationUser.build(self.user_id, user_id) | |||
| when 'Developer' | |||
| # 构建相应的团队 | |||
| team = self.owner.teams.write.take | |||
| if team.nil? | |||
| team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) | |||
| gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| end | |||
| # 设置项目在团队中的访问权限 | |||
| team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # 更改对应的团队成员 | |||
| team_user = member.team_user | |||
| $gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的 | |||
| $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id) | |||
| OrganizationUser.build(self.user_id, user_id) | |||
| when 'Reporter' | |||
| # 构建相应的团队 | |||
| team = self.owner.teams.read.take | |||
| if team.nil? | |||
| team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) | |||
| gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| end | |||
| # 设置项目在团队中的访问权限 | |||
| team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # 更改对应的团队成员 | |||
| team_user = member.team_user | |||
| $gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的 | |||
| $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id) | |||
| # 确保组织成员中有该用户 | |||
| OrganizationUser.build(self.user_id, user_id) | |||
| end | |||
| end | |||
| # if self.owner.is_a?(Organization) && member.team_user.present? | |||
| # case role&.name | |||
| # when 'Manager' | |||
| # # 构建相应的团队 | |||
| # team = self.owner.teams.admin.take | |||
| # if team.nil? | |||
| # team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) | |||
| # gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| # team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| # end | |||
| # | |||
| # # 设置项目在团队中的访问权限 | |||
| # team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| # tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # | |||
| # # 更改对应的团队成员 | |||
| # team_user = member.team_user | |||
| # $gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的 | |||
| # $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id) | |||
| # | |||
| # # 确保组织成员中有该用户 | |||
| # OrganizationUser.build(self.user_id, user_id) | |||
| # when 'Developer' | |||
| # # 构建相应的团队 | |||
| # team = self.owner.teams.write.take | |||
| # if team.nil? | |||
| # team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) | |||
| # gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| # team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| # end | |||
| # # 设置项目在团队中的访问权限 | |||
| # team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| # $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # | |||
| # # 更改对应的团队成员 | |||
| # team_user = member.team_user | |||
| # $gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的 | |||
| # $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id) | |||
| # | |||
| # OrganizationUser.build(self.user_id, user_id) | |||
| # when 'Reporter' | |||
| # # 构建相应的团队 | |||
| # team = self.owner.teams.read.take | |||
| # if team.nil? | |||
| # team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) | |||
| # gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil | |||
| # team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil? | |||
| # end | |||
| # | |||
| # # 设置项目在团队中的访问权限 | |||
| # team_project = TeamProject.build(self.user_id, team.id, self.id) | |||
| # tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil | |||
| # | |||
| # # 更改对应的团队成员 | |||
| # team_user = member.team_user | |||
| # $gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的 | |||
| # $gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的 | |||
| # team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id) | |||
| # | |||
| # # 确保组织成员中有该用户 | |||
| # OrganizationUser.build(self.user_id, user_id) | |||
| # end | |||
| # end | |||
| member.member_roles.last.update_attributes!(role: role) | |||
| end | |||
| @@ -0,0 +1,24 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: daily_platform_statistics | |||
| # | |||
| # id :integer not null, primary key | |||
| # date :date | |||
| # pv :integer default("0") | |||
| # visitor :integer default("0") | |||
| # ip :integer default("0") | |||
| # weekly_keep_rate :float(24) default("0") | |||
| # source_through :float(24) default("0") | |||
| # source_link :float(24) default("0") | |||
| # source_search :float(24) default("0") | |||
| # source_custom :float(24) default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_daily_platform_statistics_on_date (date) UNIQUE | |||
| # | |||
| class DailyPlatformStatistic < ApplicationRecord | |||
| end | |||
| @@ -0,0 +1,30 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: daily_project_statistics | |||
| # | |||
| # id :integer not null, primary key | |||
| # project_id :integer | |||
| # date :date | |||
| # score :integer default("0") | |||
| # visits :integer default("0") | |||
| # watchers :integer default("0") | |||
| # praises :integer default("0") | |||
| # forks :integer default("0") | |||
| # issues :integer default("0") | |||
| # pullrequests :integer default("0") | |||
| # commits :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # closed_issues :integer default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_daily_project_statistics_on_date (date) | |||
| # index_daily_project_statistics_on_project_id (project_id) | |||
| # | |||
| class DailyProjectStatistic < ApplicationRecord | |||
| belongs_to :project | |||
| end | |||
| @@ -6,10 +6,10 @@ | |||
| # user_id :integer not null | |||
| # number :string(255) not null | |||
| # name :string(255) not null | |||
| # card_front :integer | |||
| # card_back :integer | |||
| # hold_card_front :integer | |||
| # hold_card_back :integer | |||
| # card_front :string(255) | |||
| # card_back :string(255) | |||
| # hold_card_front :string(255) | |||
| # hold_card_back :string(255) | |||
| # state :integer default("0") | |||
| # description :string(255) | |||
| # created_at :datetime not null | |||
| @@ -24,9 +24,9 @@ class IdentityVerification < ApplicationRecord | |||
| belongs_to :user | |||
| enum state: { "待审核": 0, "已通过": 1, "已拒绝": 2} | |||
| after_create do | |||
| Attachment.where(id:[card_front,card_back,hold_card_front,hold_card_back]).update_all(is_public:0) | |||
| Attachment.where(uuid:[card_front,card_back,hold_card_front,hold_card_back]).update_all(is_public:0) | |||
| end | |||
| after_save do | |||
| if state == "已通过" | |||
| user.update(id_card_verify: true, website_permission: true) | |||
| @@ -34,18 +34,18 @@ class IdentityVerification < ApplicationRecord | |||
| end | |||
| def card_front_attachment | |||
| Attachment.find_by_id card_front | |||
| Attachment.where_id_or_uuid(card_front).first | |||
| end | |||
| def card_back_attachment | |||
| Attachment.find_by_id card_back | |||
| Attachment.where_id_or_uuid(card_back).first | |||
| end | |||
| def hold_card_front_attachment | |||
| Attachment.find_by_id hold_card_front | |||
| Attachment.where_id_or_uuid(hold_card_front).first | |||
| end | |||
| def hold_card_back_attachment | |||
| Attachment.find_by_id hold_card_back | |||
| Attachment.where_id_or_uuid(hold_card_back).first | |||
| end | |||
| end | |||
| @@ -34,12 +34,19 @@ | |||
| # ref_name :string(255) | |||
| # branch_name :string(255) | |||
| # blockchain_token_num :integer | |||
| # pm_project_id :integer | |||
| # pm_sprint_id :integer | |||
| # pm_issue_type :integer | |||
| # time_scale :decimal(10, 2) default("0.00") | |||
| # child_count :integer default("0") | |||
| # changer_id :integer | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_issues_on_assigned_to_id (assigned_to_id) | |||
| # index_issues_on_author_id (author_id) | |||
| # index_issues_on_category_id (category_id) | |||
| # index_issues_on_changer_id (changer_id) | |||
| # index_issues_on_created_on (created_on) | |||
| # index_issues_on_fixed_version_id (fixed_version_id) | |||
| # index_issues_on_priority_id (priority_id) | |||
| @@ -92,31 +99,46 @@ class Issue < ApplicationRecord | |||
| scope :closed, ->{where(status_id: 5)} | |||
| scope :opened, ->{where.not(status_id: 5)} | |||
| after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic | |||
| after_save :change_versions_count, :send_update_message_to_notice_system, :associate_attachment_container | |||
| after_save :incre_or_decre_closed_issues_count, :change_versions_count, :send_update_message_to_notice_system, :associate_attachment_container | |||
| after_destroy :update_closed_issues_count_in_project!, :decre_project_common, :decre_user_statistic, :decre_platform_statistic | |||
| def is_issuely_issue? | |||
| self.issue_classify.nil? || self.issue_classify == 'issue' | |||
| end | |||
| def incre_or_decre_closed_issues_count | |||
| if previous_changes[:status_id].present? && is_issuely_issue? | |||
| if previous_changes[:status_id][1] == 5 | |||
| CacheAsyncSetJob.perform_later("project_common_service", {closed_issues: 1}, self.project_id) | |||
| end | |||
| if previous_changes[:status_id][0] == 5 | |||
| CacheAsyncSetJob.perform_later("project_common_service", {closed_issues: -1}, self.project_id) | |||
| end | |||
| end | |||
| end | |||
| def incre_project_common | |||
| CacheAsyncSetJob.perform_later("project_common_service", {issues: 1}, self.project_id) | |||
| CacheAsyncSetJob.perform_later("project_common_service", {issues: 1}, self.project_id) if is_issuely_issue? | |||
| end | |||
| def decre_project_common | |||
| CacheAsyncSetJob.perform_later("project_common_service", {issues: -1}, self.project_id) | |||
| CacheAsyncSetJob.perform_later("project_common_service", {issues: -1}, self.project_id) if is_issuely_issue? | |||
| end | |||
| def incre_user_statistic | |||
| CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: 1}, self.author_id) | |||
| CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: 1}, self.author_id) if is_issuely_issue? | |||
| end | |||
| def decre_user_statistic | |||
| CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: -1}, self.author_id) | |||
| CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: -1}, self.author_id) if is_issuely_issue? | |||
| end | |||
| def incre_platform_statistic | |||
| CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: 1}) | |||
| CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: 1}) if is_issuely_issue? | |||
| end | |||
| def decre_platform_statistic | |||
| CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: -1}) | |||
| CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: -1}) if is_issuely_issue? | |||
| end | |||
| def get_assign_user | |||
| @@ -222,13 +244,23 @@ class Issue < ApplicationRecord | |||
| # 关附件到功能 | |||
| def associate_attachment_container | |||
| return if self.project_id == 0 | |||
| att_ids = [] | |||
| # 附件的格式为(/api/attachments/ + 附件id)的形式,提取出id进行附件属性关联,做附件访问权限控制 | |||
| att_ids += self.description.to_s.scan(/\(\/api\/attachments\/.+\)/).map{|s|s.match(/\d+/)[0]} | |||
| att_ids += self.description.to_s.scan(/\/api\/attachments\/.+\"/).map{|s|s.match(/\d+/)[0]} | |||
| att_ids += self.description.to_s.scan(/\/api\/attachments\/\d+/).map{|s|s.match(/\d+/)[0]} | |||
| if att_ids.present? | |||
| Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: "Project") | |||
| Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: 'Project') | |||
| end | |||
| att_ids2 = [] | |||
| # uuid_regex= /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ | |||
| # 附件的格式为(/api/attachments/ + uuid)的形式,提取出id进行附件属性关联,做附件访问权限控制 | |||
| att_ids2 += self.description.to_s.scan(/\(\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\)/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]} | |||
| att_ids2 += self.description.to_s.scan(/\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]} | |||
| if att_ids2.present? | |||
| Attachment.where(uuid: att_ids2).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: 'Project') | |||
| end | |||
| end | |||
| @@ -14,6 +14,7 @@ | |||
| # gid :integer | |||
| # gitea_url :string(255) | |||
| # pull_requests_count :integer default("0") | |||
| # pm_project_id :integer | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -32,7 +33,22 @@ class IssueTag < ApplicationRecord | |||
| validates :name, uniqueness: {scope: :project_id, message: "已存在" } | |||
| def self.init_data(project_id) | |||
| data = [ | |||
| data = init_issue_tag_data | |||
| data.each do |item| | |||
| next if IssueTag.exists?(project_id: project_id, name: item[0]) | |||
| IssueTag.create!(project_id: project_id, name: item[0], description: item[1], color: item[2]) | |||
| end | |||
| $redis_cache.hset("project_init_issue_tags", project_id, 1) | |||
| end | |||
| def reset_counter_field | |||
| self.update_column(:issues_count, issue_issues.size) | |||
| self.update_column(:pull_requests_count, pull_request_issues.size) | |||
| end | |||
| def self.init_issue_tag_data | |||
| [ | |||
| ["缺陷", "表示存在意外问题或错误", "#d92d4c"], | |||
| ["功能", "表示新功能申请", "#ee955a"], | |||
| ["疑问", "表示存在疑惑", "#2d6ddc"], | |||
| @@ -44,16 +60,18 @@ class IssueTag < ApplicationRecord | |||
| ["测试", "表示需要测试的需求", "#2897b9"], | |||
| ["重复", "表示已存在类似的疑修", "#bb5332"] | |||
| ] | |||
| data.each do |item| | |||
| next if IssueTag.exists?(project_id: project_id, name: item[0]) | |||
| IssueTag.create!(project_id: project_id, name: item[0], description: item[1], color: item[2]) | |||
| end | |||
| $redis_cache.hset("project_init_issue_tags", project_id, 1) | |||
| end | |||
| def reset_counter_field | |||
| self.update_column(:issues_count, issue_issues.size) | |||
| self.update_column(:pull_requests_count, pull_request_issues.size) | |||
| def self.init_mp_issues_tags | |||
| data = {"total_count": 10,} | |||
| data["issue_tags"] = init_issue_tag_data.map{|item| | |||
| { | |||
| "name": item[0], | |||
| "description": item[1], | |||
| "color": item[2], | |||
| } | |||
| } | |||
| data | |||
| end | |||
| @@ -27,6 +27,7 @@ | |||
| # | |||
| # index_journals_on_created_on (created_on) | |||
| # index_journals_on_journalized_id (journalized_id) | |||
| # index_journals_on_parent_id (parent_id) | |||
| # index_journals_on_review_id (review_id) | |||
| # index_journals_on_user_id (user_id) | |||
| # journals_journalized_id (journalized_id,journalized_type) | |||
| @@ -61,6 +62,7 @@ class Journal < ApplicationRecord | |||
| # 关附件到功能 | |||
| def associate_attachment_container | |||
| return if self.issue&.project_id.to_i == 0 | |||
| att_ids = [] | |||
| # 附件的格式为(/api/attachments/ + 附件id)的形式,提取出id进行附件属性关联,做附件访问权限控制 | |||
| att_ids += self.notes.to_s.scan(/\(\/api\/attachments\/.+\)/).map{|s|s.match(/\d+/)[0]} | |||
| @@ -69,6 +71,15 @@ class Journal < ApplicationRecord | |||
| if att_ids.present? | |||
| Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Journal'").update_all(container_id: self.issue.project_id, container_type: "Project") | |||
| end | |||
| att_ids2 = [] | |||
| # uuid_regex= /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ | |||
| # 附件的格式为(/api/attachments/ + uuid)的形式,提取出id进行附件属性关联,做附件访问权限控制 | |||
| att_ids2 += self.notes.to_s.scan(/\(\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\)/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]} | |||
| att_ids2 += self.notes.to_s.scan(/\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]} | |||
| if att_ids2.present? | |||
| Attachment.where(uuid: att_ids).where("container_type IS NULL OR container_type = 'Journal'").update_all(container_id: self.issue.project_id, container_type: "Project") | |||
| end | |||
| end | |||
| def operate_content | |||
| @@ -78,9 +89,9 @@ class Journal < ApplicationRecord | |||
| when 'issue' | |||
| return "创建了<b>疑修</b>" | |||
| when 'attachment' | |||
| old_value = Attachment.where(id: detail.old_value.split(",")).pluck(:filename).join("、") | |||
| new_value = Attachment.where(id: detail.value.split(",")).pluck(:filename).join("、") | |||
| if old_value.nil? || old_value.blank? | |||
| old_value = Attachment.where("id in (?) or uuid in (?)", detail.old_value.to_s.split(","), detail.old_value.to_s.split(",")).pluck(:filename).join("、") | |||
| new_value = Attachment.where("id in (?) or uuid in (?)", detail.value.to_s.split(","), detail.value.to_s.split(",")).pluck(:filename).join("、") | |||
| if old_value.nil? || old_value.blank? | |||
| content += "添加了<b>#{new_value}</b>附件" | |||
| else | |||
| new_value = "无" if new_value.blank? | |||
| @@ -52,6 +52,8 @@ class MessageTemplate < ApplicationRecord | |||
| self.create(type: 'MessageTemplate::ProjectMilestone', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 创建了一个里程碑:<b>{name}</b>', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}', email: email_html, email_title: "#{PLATFORM}: {nickname1} 在 {nickname2}/{repository} 新建了一个里程碑") | |||
| email_html = File.read("#{email_template_html_dir}/project_milestone_completed.html") | |||
| self.create(type: 'MessageTemplate::ProjectMilestoneCompleted', sys_notice: '在 <b>{nickname}/{repository}</b> 仓库,里程碑 <b>{name}</b> 的完成度已达到100%', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}', email: email_html, email_title: "#{PLATFORM}: 仓库 {nickname}/{repository} 有里程碑已完成") | |||
| self.create(type: 'MessageTemplate::ProjectMilestoneEarlyExpired', sys_notice: '您创建的里程碑 <b>{name}</b> 已临近截止日期,请尽快处理.', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}') | |||
| self.create(type: 'MessageTemplate::ProjectMilestoneExpired', sys_notice: '您创建的里程碑 <b>{name}</b> 已逾期,请及时更新进度或联系项目团队.', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}') | |||
| self.create(type: 'MessageTemplate::ProjectPraised', sys_notice: '<b>{nickname1}</b> 点赞了你管理的仓库 <b>{nickname2}/{repository}</b>', notification_url: '{baseurl}/{login}') | |||
| self.create(type: 'MessageTemplate::ProjectOpenDevOps', sys_notice: '您的仓库 <b>{repository}</b> 已成功开通引擎服务,可通过简单的节点编排完成自动化集成与部署。欢迎体验!', notification_url: '{baseurl}/{owner}/{identifier}/devops') | |||
| email_html = File.read("#{email_template_html_dir}/project_pull_request.html") | |||
| @@ -0,0 +1,70 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: message_templates | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # sys_notice :text(65535) | |||
| # email :text(65535) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # notification_url :string(255) | |||
| # email_title :string(255) | |||
| # | |||
| # 我管理的仓库有里程碑完成 | |||
| class MessageTemplate::ProjectMilestoneEarlyExpired < MessageTemplate | |||
| # MessageTemplate::ProjectMilestoneEarlyExpired.get_message_content(User.where(login: 'yystopf'), Version.find(7)) | |||
| def self.get_message_content(receivers, milestone) | |||
| receivers.each do |receiver| | |||
| if receiver.user_template_message_setting.present? | |||
| send_setting = receiver.user_template_message_setting.notification_body["ManageProject::MilestoneExpired"] | |||
| send_setting = send_setting.nil? ? UserTemplateMessageSetting.init_notification_body["ManageProject::MilestoneExpired"] : send_setting | |||
| receivers = receivers.where.not(id: receiver.id) unless send_setting | |||
| end | |||
| end | |||
| return '', '', '' if receivers.blank? | |||
| project = milestone&.project | |||
| owner = project&.owner | |||
| content = sys_notice.gsub('{nickname}', owner&.real_name).gsub('{repository}', project&.name).gsub('{name}', milestone&.name) | |||
| url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', milestone&.id.to_s) | |||
| return receivers_string(receivers), content, url | |||
| rescue => e | |||
| Rails.logger.info("MessageTemplate::MilestoneEarlyExpired.get_message_content [ERROR] #{e}") | |||
| return '', '', '' | |||
| end | |||
| def self.get_email_message_content(receiver, milestone) | |||
| if receiver.user_template_message_setting.present? | |||
| send_setting = receiver.user_template_message_setting.email_body["ManageProject::MilestoneExpired"] | |||
| send_setting = send_setting.nil? ? UserTemplateMessageSetting.init_email_body["ManageProject::MilestoneExpired"] : send_setting | |||
| return '', '', '' unless send_setting | |||
| project = milestone&.project | |||
| owner = project&.owner | |||
| title = email_title | |||
| title.gsub!('{nickname}', owner&.real_name) | |||
| title.gsub!('{repository}', project&.name) | |||
| content = email | |||
| content.gsub!('{receiver}', receiver&.real_name) | |||
| content.gsub!('{baseurl}', base_url) | |||
| content.gsub!('{nickname}', owner&.real_name) | |||
| content.gsub!('{repository}', project&.name) | |||
| content.gsub!('{login}', owner&.login) | |||
| content.gsub!('{identifier}', project&.identifier) | |||
| content.gsub!('{id}', milestone&.id.to_s) | |||
| content.gsub!('{name}', milestone&.name) | |||
| content.gsub!('{platform}', PLATFORM) | |||
| return receiver&.mail, title, content | |||
| else | |||
| return '', '', '' | |||
| end | |||
| rescue => e | |||
| Rails.logger.info("MessageTemplate::MilestoneEarlyExpired.get_email_message_content [ERROR] #{e}") | |||
| return '', '', '' | |||
| end | |||
| end | |||
| @@ -0,0 +1,70 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: message_templates | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # sys_notice :text(65535) | |||
| # email :text(65535) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # notification_url :string(255) | |||
| # email_title :string(255) | |||
| # | |||
| # 我管理的仓库有里程碑完成 | |||
| class MessageTemplate::ProjectMilestoneExpired < MessageTemplate | |||
| # MessageTemplate::ProjectMilestoneExpired.get_message_content(User.where(login: 'yystopf'), Version.find(7)) | |||
| def self.get_message_content(receivers, milestone) | |||
| receivers.each do |receiver| | |||
| if receiver.user_template_message_setting.present? | |||
| send_setting = receiver.user_template_message_setting.notification_body["ManageProject::MilestoneExpired"] | |||
| send_setting = send_setting.nil? ? UserTemplateMessageSetting.init_notification_body["ManageProject::MilestoneExpired"] : send_setting | |||
| receivers = receivers.where.not(id: receiver.id) unless send_setting | |||
| end | |||
| end | |||
| return '', '', '' if receivers.blank? | |||
| project = milestone&.project | |||
| owner = project&.owner | |||
| content = sys_notice.gsub('{nickname}', owner&.real_name).gsub('{repository}', project&.name).gsub('{name}', milestone&.name) | |||
| url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', milestone&.id.to_s) | |||
| return receivers_string(receivers), content, url | |||
| rescue => e | |||
| Rails.logger.info("MessageTemplate::ProjectMilestoneExpired.get_message_content [ERROR] #{e}") | |||
| return '', '', '' | |||
| end | |||
| def self.get_email_message_content(receiver, milestone) | |||
| if receiver.user_template_message_setting.present? | |||
| send_setting = receiver.user_template_message_setting.email_body["ManageProject::MilestoneExpired"] | |||
| send_setting = send_setting.nil? ? UserTemplateMessageSetting.init_email_body["ManageProject::MilestoneExpired"] : send_setting | |||
| return '', '', '' unless send_setting | |||
| project = milestone&.project | |||
| owner = project&.owner | |||
| title = email_title | |||
| title.gsub!('{nickname}', owner&.real_name) | |||
| title.gsub!('{repository}', project&.name) | |||
| content = email | |||
| content.gsub!('{receiver}', receiver&.real_name) | |||
| content.gsub!('{baseurl}', base_url) | |||
| content.gsub!('{nickname}', owner&.real_name) | |||
| content.gsub!('{repository}', project&.name) | |||
| content.gsub!('{login}', owner&.login) | |||
| content.gsub!('{identifier}', project&.identifier) | |||
| content.gsub!('{id}', milestone&.id.to_s) | |||
| content.gsub!('{name}', milestone&.name) | |||
| content.gsub!('{platform}', PLATFORM) | |||
| return receiver&.mail, title, content | |||
| else | |||
| return '', '', '' | |||
| end | |||
| rescue => e | |||
| Rails.logger.info("MessageTemplate::ProjectMilestoneExpired.get_email_message_content [ERROR] #{e}") | |||
| return '', '', '' | |||
| end | |||
| end | |||
| @@ -0,0 +1,27 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: open_users | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer | |||
| # type :string(255) | |||
| # uid :string(255) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # extra :text(65535) | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_open_users_on_type_and_uid (type,uid) UNIQUE | |||
| # index_open_users_on_user_id (user_id) | |||
| # | |||
| class OpenUsers::Acge < OpenUser | |||
| def nickname | |||
| extra&.[]('nickname') | |||
| end | |||
| def en_type | |||
| 'acge' | |||
| end | |||
| end | |||
| @@ -64,7 +64,7 @@ | |||
| class Organization < Owner | |||
| alias_attribute :name, :login | |||
| NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| NAME_REGEX = /^[a-zA-Z0-9]+([-_.][a-zA-Z0-9]+)*$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| default_scope { where(type: "Organization") } | |||
| @@ -79,7 +79,7 @@ class Organization < Owner | |||
| validates :login, presence: true | |||
| validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, case_sensitive: false | |||
| validates :login, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :login, format: { with: NAME_REGEX, multiline: true, message: "只能以数字或字母开头,仅支持横杠、下划线、点三种符号,不允许符号连续排列,长度4-50个字符" } | |||
| delegate :description, :website, :location, :repo_admin_change_team_access, :recommend, | |||
| :visibility, :max_repo_creation, :num_projects, :num_users, :num_teams, | |||
| @@ -182,14 +182,6 @@ class Organization < Owner | |||
| organization_users.count | |||
| end | |||
| def teams_count | |||
| teams.count | |||
| end | |||
| def organization_users_count | |||
| organization_users.count | |||
| end | |||
| def real_name | |||
| name = lastname + firstname | |||
| name = name.blank? ? (nickname.blank? ? login : nickname) : name | |||
| @@ -217,4 +209,11 @@ class Organization < Owner | |||
| enabling_cla == true | |||
| end | |||
| def num_users | |||
| organization_user_ids = self.organization_users.pluck(:user_id).uniq | |||
| project_member_user_ids = self.projects.joins(:members).pluck("members.user_id").uniq | |||
| ids = organization_user_ids + project_member_user_ids | |||
| ids.uniq.size | |||
| end | |||
| end | |||
| @@ -34,6 +34,10 @@ class Page < ApplicationRecord | |||
| PageService.genernate_user(user_id) | |||
| end | |||
| before_destroy do | |||
| PageService.close_site(user_id, identifier) | |||
| end | |||
| before_save do | |||
| if state_changed? && state == false | |||
| PageService.close_site(user_id, identifier) | |||
| @@ -46,7 +50,7 @@ class Page < ApplicationRecord | |||
| def url | |||
| @deploy_domain = EduSetting.find_by_name("site_page_deploy_domain").try(:value) | |||
| "http://#{user.login}.#{@deploy_domain}/#{identifier}" | |||
| "http://#{identifier}" | |||
| end | |||
| def build_script_path | |||
| @@ -136,6 +136,7 @@ class Project < ApplicationRecord | |||
| has_many :project_topic_ralates, dependent: :destroy | |||
| has_many :project_topics, through: :project_topic_ralates | |||
| has_many :commit_logs, dependent: :destroy | |||
| has_many :daily_project_statistics, dependent: :destroy | |||
| after_create :incre_user_statistic, :incre_platform_statistic | |||
| after_save :check_project_members | |||
| before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data | |||
| @@ -28,5 +28,6 @@ class TemplateMessageSetting::CreateOrAssign < TemplateMessageSetting | |||
| self.find_or_create_by(name: "疑修状态变更", key: "IssueChanged") | |||
| self.find_or_create_by(name: "合并请求状态变更", key: "PullRequestChanged") | |||
| self.find_or_create_by(name: "疑修截止日期到达最后一天", key: "IssueExpire", notification_disabled: false) | |||
| self.find_or_create_by(name: "里程碑逾期提醒", key: "MilestoneExpired", notification_disabled: false, email_disabled: true) | |||
| end | |||
| end | |||
| @@ -43,8 +43,9 @@ class TraceUser < ApplicationRecord | |||
| def build_token | |||
| return if username.blank? || password.blank? || unit.blank? || email.blank? || name.blank? | |||
| response = Trace::AddUserService.call(username, password, unit, telnumber, email, name) | |||
| self.token = response[1]['token'] | |||
| response1 = Trace::AddUserService.call(username, password, unit, telnumber, email, name) | |||
| response2 = Trace::LoginService.call(username, password) | |||
| self.token = response2[1]['token'] | |||
| self.expired_at = Time.now + 1.hours | |||
| end | |||
| @@ -115,7 +115,7 @@ class User < Owner | |||
| # trustie: 来自Trustie平台 | |||
| # forge: 平台本身注册的用户 | |||
| # military: 军科的用户 | |||
| enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat, :bot], default: :forge, scope: :shallow | |||
| enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat, :bot, :acge], default: :forge, scope: :shallow | |||
| belongs_to :laboratory, optional: true | |||
| has_one :user_extension, dependent: :destroy | |||
| @@ -189,7 +189,7 @@ class User < Owner | |||
| has_many :user_clas, :dependent => :destroy | |||
| has_many :clas, through: :user_clas | |||
| has_many :pages, :dependent => :destroy | |||
| has_one :page, :dependent => :destroy | |||
| # Groups and active users | |||
| scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } | |||
| @@ -465,7 +465,7 @@ class User < Owner | |||
| $gitea_client.delete_users_tokens_by_username_token(self.login, e["name"], {query: {sudo: self.login} }) | |||
| } | |||
| end | |||
| new_result = $gitea_client.post_users_tokens_by_username(self.login, { query: {sudo: self.login}, body:{ name: self.login} }) | |||
| new_result = $gitea_client.post_users_tokens_by_username(self.login, { query: {sudo: self.login}, body:{ name: "#{self.login}-#{SecureRandom.hex(6)}", scopes: ["all"]}.to_json }) | |||
| if new_result["sha1"].present? | |||
| update(gitea_token: new_result["sha1"]) | |||
| end | |||
| @@ -773,7 +773,7 @@ class User < Owner | |||
| def check_website_permission | |||
| if website_permission_changed? && website_permission == false | |||
| self.pages.update_all(state: false, state_description:"因违规使用,现关闭Page服务") | |||
| self.page.update(state: false, state_description:"因违规使用,现关闭Page服务") | |||
| PageService.close_site(self.id) | |||
| end | |||
| end | |||
| @@ -36,6 +36,7 @@ class UserTemplateMessageSetting < ApplicationRecord | |||
| "CreateOrAssign::IssueChanged": true, | |||
| "CreateOrAssign::PullRequestChanged": true, | |||
| "CreateOrAssign::IssueExpire": true, | |||
| "CreateOrAssign::MilestoneExpired": true, | |||
| "ManageProject::Issue": true, | |||
| "ManageProject::PullRequest": true, | |||
| "ManageProject::Member": true, | |||
| @@ -44,6 +45,8 @@ class UserTemplateMessageSetting < ApplicationRecord | |||
| "ManageProject::Forked": true, | |||
| "ManageProject::Milestone": true, | |||
| "ManageProject::MilestoneCompleted": true, | |||
| "ManageProject::MilestoneExpired": true, | |||
| "ManageProject::MilestoneEarlyExpired": true, | |||
| }.stringify_keys! | |||
| end | |||
| @@ -57,6 +60,7 @@ class UserTemplateMessageSetting < ApplicationRecord | |||
| "CreateOrAssign::IssueChanged": false, | |||
| "CreateOrAssign::PullRequestChanged": false, | |||
| "CreateOrAssign::IssueExpire": false, | |||
| "CreateOrAssign::MilestoneExpired": false, | |||
| "ManageProject::Issue": false, | |||
| "ManageProject::PullRequest": false, | |||
| "ManageProject::Member": false, | |||
| @@ -65,6 +69,8 @@ class UserTemplateMessageSetting < ApplicationRecord | |||
| "ManageProject::Forked": false, | |||
| "ManageProject::Milestone": false, | |||
| "ManageProject::MilestoneCompleted": false, | |||
| "ManageProject::MilestoneExpired": false, | |||
| "ManageProject::MilestoneEarlyExpired": false, | |||
| }.stringify_keys! | |||
| end | |||
| @@ -68,5 +68,7 @@ class Version < ApplicationRecord | |||
| def send_update_message_to_notice_system | |||
| SendTemplateMessageJob.perform_later('ProjectMilestoneCompleted', self.id) if Site.has_notice_menu? && self.issue_percent == 1.0 | |||
| SendTemplateMessageJob.perform_later('ProjectMilestoneEarlyExpired', self.id) if Site.has_notice_menu? && self.effective_date == Date.today + 1.days | |||
| SendTemplateMessageJob.perform_later('ProjectMilestoneExpired', self.id) if Site.has_notice_menu? && self.effective_date == Date.today - 1.days | |||
| end | |||
| end | |||
| @@ -31,8 +31,8 @@ module Api::V1::Issues::Concerns::Checkable | |||
| def check_attachments (attachment_ids) | |||
| raise ApplicationService::Error, "请输入正确的附件ID数组!" unless attachment_ids.is_a?(Array) | |||
| attachment_ids.each do |aid| | |||
| raise ApplicationService::Error, "请输入正确的附件ID!" unless Attachment.exists?(id: aid) | |||
| end | |||
| raise ApplicationService::Error, "请输入正确的附件ID!" unless Attachment.where_id_or_uuid(aid).exists? | |||
| end | |||
| end | |||
| def check_atme_receivers(receivers_login) | |||
| @@ -9,7 +9,7 @@ module Api::V1::Issues::Concerns::Loadable | |||
| end | |||
| def load_attachments(attachment_ids) | |||
| @attachments = Attachment.where(id: attachment_ids) | |||
| @attachments = Attachment.where("id in (?) or uuid in (?)", attachment_ids, attachment_ids) | |||
| end | |||
| def load_atme_receivers(receivers_login) | |||
| @@ -60,13 +60,31 @@ class Api::V1::Issues::ListService < ApplicationService | |||
| issues = issues.where(author_id: author_id) if author_id.present? | |||
| # issue_tag_ids | |||
| issues = issues.ransack(issue_tags_value_cont: issue_tag_ids.sort!.join(',')).result unless issue_tag_ids.blank? | |||
| if issue_tag_ids.present? | |||
| if issue_tag_ids.include?('-1') | |||
| issues = issues.where(issue_tags_value: nil).or(issues.where(issue_tags_value: "")) | |||
| else | |||
| issues = issues.ransack(issue_tags_value_cont: issue_tag_ids.sort!.join(',')).result | |||
| end | |||
| end | |||
| # milestone_id | |||
| issues = issues.where(fixed_version_id: milestone_id) if milestone_id.present? | |||
| if milestone_id.present? | |||
| if milestone_id.to_i == -1 | |||
| issues = issues.where(fixed_version_id: nil) | |||
| else | |||
| issues = issues.where(fixed_version_id: milestone_id) | |||
| end | |||
| end | |||
| # assigner_id | |||
| issues = issues.joins(:assigners).where(users: {id: assigner_id}) if assigner_id.present? | |||
| if assigner_id.present? | |||
| if assigner_id.to_i == -1 | |||
| issues = issues.left_joins(:assigners).where(users: {id: nil}) | |||
| else | |||
| issues = issues.joins(:assigners).where(users: {id: assigner_id}) | |||
| end | |||
| end | |||
| # status_id | |||
| issues = issues.where(status_id: status_id) if status_id.present? && category != 'closed' | |||
| @@ -0,0 +1,42 @@ | |||
| class Api::V1::Projects::Actions::Runs::JobShowService < ApplicationService | |||
| include ActiveModel::Model | |||
| attr_reader :project, :token, :owner, :repo, :run, :job, :log_cursors | |||
| attr_accessor :gitea_data | |||
| validates :run, :job, :log_cursors, presence: true | |||
| def initialize(project, run, job, log_cursors, token = nil) | |||
| @project = project | |||
| @owner = project&.owner.login | |||
| @repo = project&.identifier | |||
| @run = run | |||
| @job = job | |||
| @log_cursors = log_cursors | |||
| @token = token | |||
| end | |||
| def call | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| load_gitea_data | |||
| @gitea_data | |||
| end | |||
| private | |||
| def request_params | |||
| { | |||
| access_token: token | |||
| } | |||
| end | |||
| def request_body | |||
| { | |||
| logCursors: log_cursors | |||
| } | |||
| end | |||
| def load_gitea_data | |||
| @gitea_data = $gitea_hat_client.post_repos_actions_runs_jobs_by_owner_repo_run_job(owner, repo, run, job, {query: request_params, body: request_body.to_json}) | |||
| end | |||
| end | |||
| @@ -0,0 +1,40 @@ | |||
| class Api::V1::Projects::Actions::Runs::ListService < ApplicationService | |||
| include ActiveModel::Model | |||
| attr_reader :project, :token, :owner, :repo, :workflow, :page, :limit | |||
| attr_accessor :gitea_data | |||
| validates :workflow, presence: true | |||
| def initialize(project, params, token =nil) | |||
| @project = project | |||
| @owner = project&.owner.login | |||
| @repo = project&.identifier | |||
| @workflow = params[:workflow] | |||
| @page = params[:page] || 1 | |||
| @limit = params[:limit] || 15 | |||
| @token = token | |||
| end | |||
| def call | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| load_gitea_data | |||
| @gitea_data | |||
| end | |||
| private | |||
| def request_params | |||
| { | |||
| access_token: token, | |||
| workflow: workflow, | |||
| page: page, | |||
| limit: limit | |||
| } | |||
| end | |||
| def load_gitea_data | |||
| @gitea_data = $gitea_hat_client.get_repos_actions_by_owner_repo(owner, repo, {query: request_params}) rescue nil | |||
| raise Error, '获取流水线执行记录失败!' unless @gitea_data.is_a?(Hash) | |||
| end | |||
| end | |||
| @@ -32,7 +32,7 @@ class Api::V1::Projects::Branches::DeleteService < ApplicationService | |||
| def excute_data_to_gitea | |||
| begin | |||
| @gitea_data = $gitea_client.delete_repos_branches_by_owner_repo_branch(owner, repo, branch_name, {query: request_params}) | |||
| @gitea_data = $gitea_client.delete_repos_branches_by_owner_repo_branch(owner, repo, CGI.escape(branch_name), {query: request_params}) | |||
| rescue => e | |||
| raise Error, '保护分支无法删除!' if e.to_s.include?("branch protected") | |||
| raise Error, '删除分支失败!' | |||
| @@ -1,6 +1,6 @@ | |||
| class Api::V1::Projects::Branches::ListService < ApplicationService | |||
| attr_accessor :project, :token, :owner, :repo, :name, :page, :limit | |||
| attr_accessor :project, :token, :owner, :repo, :name, :state, :page, :limit | |||
| attr_accessor :gitea_data, :gitea_repo_data | |||
| def initialize(project, params, token=nil) | |||
| @@ -9,6 +9,7 @@ class Api::V1::Projects::Branches::ListService < ApplicationService | |||
| @repo = project&.identifier | |||
| @token = token | |||
| @name = params[:name] | |||
| @state = params[:state] | |||
| @page = params[:page] | |||
| @limit = params[:limit] | |||
| end | |||
| @@ -18,7 +19,6 @@ class Api::V1::Projects::Branches::ListService < ApplicationService | |||
| load_default_branch | |||
| @gitea_data[:default_branch] = @gitea_repo_data["default_branch"] | |||
| @gitea_data | |||
| end | |||
| @@ -30,7 +30,8 @@ class Api::V1::Projects::Branches::ListService < ApplicationService | |||
| limit: limit | |||
| } | |||
| params.merge!({name: name}) if name.present? | |||
| params.merge!({state: state}) if state.present? | |||
| params | |||
| end | |||
| @@ -0,0 +1,47 @@ | |||
| class Api::V1::Projects::Branches::RestoreService < ApplicationService | |||
| include ActiveModel::Model | |||
| attr_accessor :project, :token, :owner, :repo, :branch_id, :branch_name | |||
| attr_accessor :gitea_data | |||
| validates :branch_id, :branch_name, presence: true | |||
| def initialize(project, branch_id, branch_name, token= nil) | |||
| @project = project | |||
| @owner = project&.owner&.login | |||
| @repo = project&.identifier | |||
| @branch_id = branch_id | |||
| @branch_name = branch_name | |||
| @token = token | |||
| end | |||
| def call | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| excute_data_to_gitea | |||
| true | |||
| end | |||
| private | |||
| def request_params | |||
| { | |||
| access_token: token | |||
| } | |||
| end | |||
| def request_body | |||
| { | |||
| branch_id: branch_id, | |||
| name: branch_name, | |||
| } | |||
| end | |||
| def excute_data_to_gitea | |||
| begin | |||
| @gitea_data = $gitea_hat_client.post_repos_branches_restore_by_owner_repo(owner, repo, {query: request_params, body: request_body.to_json}) | |||
| rescue => e | |||
| raise Error, '恢复分支失败!' | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,39 @@ | |||
| class Api::V1::Projects::Commits::RecentService < ApplicationService | |||
| attr_reader :project, :page, :limit, :keyword, :owner, :repo, :token | |||
| attr_accessor :gitea_data | |||
| def initialize(project, params, token=nil) | |||
| @project = project | |||
| @page = params[:page] || 1 | |||
| @limit = params[:limit] || 15 | |||
| @keyword = params[:keyword] | |||
| @owner = project&.owner&.login | |||
| @repo = project&.identifier | |||
| @token = token | |||
| end | |||
| def call | |||
| load_gitea_data | |||
| gitea_data | |||
| end | |||
| private | |||
| def request_params | |||
| param = { | |||
| access_token: token, | |||
| page: page, | |||
| limit: limit | |||
| } | |||
| param.merge!(keyword: keyword) if keyword.present? | |||
| param | |||
| end | |||
| def load_gitea_data | |||
| @gitea_data = $gitea_hat_client.get_repos_recent_commits_by_owner_repo(owner, repo, {query: request_params}) rescue nil | |||
| raise Error, "获取最近提交列表失败" unless @gitea_data.is_a?(Hash) | |||
| end | |||
| end | |||
| @@ -29,6 +29,6 @@ class Api::V1::Projects::CompareService < ApplicationService | |||
| end | |||
| def load_gitea_data | |||
| @gitea_data = $gitea_client.get_repos_compare_by_owner_repo_from_to(owner, repo, from, to, {query: request_params}) rescue nil | |||
| @gitea_data = $gitea_hat_client.get_repos_compare_by_owner_repo_baseref_headref(owner, repo, to, from, {query: request_params}) rescue nil | |||
| end | |||
| end | |||
| @@ -32,7 +32,7 @@ class Api::V1::Projects::Tags::DeleteService < ApplicationService | |||
| def excute_data_to_gitea | |||
| begin | |||
| @gitea_data = $gitea_client.delete_repos_tags_by_owner_repo_tag(owner, repo, tag_name, {query: request_params}) | |||
| @gitea_data = $gitea_client.delete_repos_tags_by_owner_repo_tag(owner, repo, CGI.escape(tag_name), {query: request_params}) | |||
| rescue => e | |||
| raise Error, '请先删除发行版!' if e.to_s.include?("409") | |||
| raise Error, '删除标签失败!' | |||
| @@ -0,0 +1,48 @@ | |||
| class Api::V1::Projects::Tags::GetService < ApplicationService | |||
| include ActiveModel::Model | |||
| attr_reader :project, :token, :owner, :repo, :tag_name | |||
| attr_accessor :gitea_data | |||
| validates :tag_name, presence: true | |||
| def initialize(project, tag_name, token=nil) | |||
| @project = project | |||
| @owner = project&.owner&.login | |||
| @repo = project&.identifier | |||
| @tag_name = tag_name.to_s | |||
| @token = token | |||
| end | |||
| def call | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| check_tag_exist | |||
| load_gitea_data | |||
| gitea_data | |||
| end | |||
| private | |||
| def request_params | |||
| params = { | |||
| access_token: token | |||
| } | |||
| params | |||
| end | |||
| def load_gitea_data | |||
| @gitea_data = $gitea_hat_client.get_repos_tags_by_owner_repo_tag(owner, repo, URI.escape(tag_name), {query: request_params}) rescue nil | |||
| raise Error, '获取标签失败!' unless @gitea_data.is_a?(Hash) | |||
| end | |||
| def check_tag_exist | |||
| result = $gitea_hat_client.get_repos_tag_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil | |||
| raise Error, '查询标签名称失败!' unless result.is_a?(Array) | |||
| raise Error, '标签不存在!' if !result.include?(@tag_name) | |||
| end | |||
| end | |||
| @@ -0,0 +1,221 @@ | |||
| module Baidu | |||
| class TongjiService < ApplicationService | |||
| attr_reader :client_id, :client_secret, :site_id | |||
| # login、code、password、password_confirmation | |||
| def initialize | |||
| @client_id = "6dMO2kqKUaMZkBrMaUMxQSNAT49v0Mjq" | |||
| @client_secret = "qvWqF33AOmGs1tPCgsROvis9EQCuNmd3" | |||
| @site_id = 18657013 | |||
| end | |||
| def call | |||
| end | |||
| def init_overview_data_by(start_date = nil, end_date = nil) | |||
| start_date = Time.now.prev_year.beginning_of_year if start_date.nil? | |||
| end_date = Time.now | |||
| Rails.logger.info("*********开始百度统计-概览:#{start_date}-#{end_date}*********") | |||
| sql_connection = ActiveRecord::Base.connection | |||
| sql_connection.begin_db_transaction | |||
| # 如果存在数据 先清空 | |||
| # sql_connection.execute("delete from daily_platform_statistics where date between '#{start_date}' and '#{end_date}'") | |||
| multiple_days_data = overview_multiple_days_data(start_date, end_date) | |||
| if multiple_days_data.present? | |||
| sql = "replace into daily_platform_statistics (date,pv,visitor,ip,created_at,updated_at) values #{multiple_days_data.join(",")}" | |||
| sql_connection.execute(sql) | |||
| end | |||
| sql_connection.commit_db_transaction | |||
| Rails.logger.info("*********结束百度统计-概览:#{start_date}-#{end_date}*********") | |||
| end | |||
| def init_source_from_data_by(start_date = nil, end_date = nil) | |||
| start_date = Time.now.prev_year.beginning_of_year if start_date.nil? | |||
| end_date = Time.now | |||
| Rails.logger.info("*********开始百度统计-来源:#{start_date}-#{end_date}*********") | |||
| source_from_batch_add(start_date, end_date) | |||
| Rails.logger.info("*********结束百度统计-来源:#{start_date}-#{end_date}*********") | |||
| end | |||
| # 按日期获取来源数据 | |||
| def source_from_batch_add(start_date,end_date) | |||
| # 补充更新开始时间的当天数据 | |||
| source_from_by_date(start_date) | |||
| diff_days(start_date, end_date).times.each do |t| | |||
| new_start_date = start_date + (t + 1).days | |||
| source_from_by_date(new_start_date) | |||
| end | |||
| # 补充更新最后时间一天数据 | |||
| source_from_by_date(end_date) | |||
| end | |||
| # 按天获取来源数据 | |||
| def source_from_by_date(start_date) | |||
| return [] unless access_token.present? && start_date.present? | |||
| source_from_data = api("source/all/a", start_date, start_date, "pv_count,visitor_count,ip_count") | |||
| source_from = [] | |||
| source_from_data['items'][1].each_with_index do |source, index| | |||
| source_from.push(((source[0].to_f / source_from_data['sum'][0][0].to_f) * 100).round(2)) | |||
| end | |||
| daily_statistic = DailyPlatformStatistic.find_or_initialize_by(date: start_date) | |||
| daily_statistic.source_through = source_from[0] | |||
| daily_statistic.source_link = source_from[1] | |||
| daily_statistic.source_search = source_from[2] | |||
| daily_statistic.source_custom = source_from[3] | |||
| daily_statistic.save | |||
| end | |||
| def diff_days(start_date, end_date) | |||
| (end_date.beginning_of_day.to_i - start_date.beginning_of_day.to_i) / (24 * 3600) | |||
| end | |||
| def overview_batch_add(start_date, end_date) | |||
| return [] unless access_token.present? && start_date.present? && end_date.present? | |||
| start_date = Time.now - 1.days if start_date.strftime("%Y%m%d") == end_date.strftime("%Y%m%d") | |||
| overview_data = api("overview/getTimeTrendRpt", start_date, end_date, "pv_count,visitor_count,ip_count") | |||
| overview_data['items'][0].each_with_index do |date, index| | |||
| pv = overview_data['items'][1][index][0] | |||
| visitor = overview_data['items'][1][index][1] | |||
| ip = overview_data['items'][1][index][2] | |||
| job_date = date[0].to_s.gsub("/", "-") | |||
| daily_statistic = DailyPlatformStatistic.find_or_initialize_by(date: job_date) | |||
| daily_statistic.date = job_date | |||
| daily_statistic.pv = pv | |||
| daily_statistic.visitor = visitor | |||
| daily_statistic.ip = ip | |||
| daily_statistic.save | |||
| end | |||
| overview_data | |||
| end | |||
| def overview_multiple_days_data(start_date, end_date) | |||
| return [] unless access_token.present? && start_date.present? && end_date.present? | |||
| overview_data = api("overview/getTimeTrendRpt", start_date, end_date, "pv_count,visitor_count,ip_count") | |||
| data = [] | |||
| created_at = Time.now.strftime("%Y-%m-%d 00:00:00") | |||
| overview_data['items'][0].each_with_index do |date, index| | |||
| pv = overview_data['items'][1][index][0] | |||
| visitor = overview_data['items'][1][index][1] | |||
| ip = overview_data['items'][1][index][2] | |||
| data.push("('#{date[0].to_s.gsub("/", "-")}', #{pv.to_s.gsub("--","0")}, #{visitor.to_s.gsub("--","0")}, #{ip.to_s.gsub("--","0")},\"#{created_at}\",\"#{created_at}\")") | |||
| end | |||
| data | |||
| end | |||
| def code_url | |||
| "http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=#{client_id}&redirect_uri=oob&scope=basic&display=popup" | |||
| end | |||
| def oauth_url(code) | |||
| "http://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&code=#{code}&client_id=#{client_id}&client_secret=#{client_secret}&redirect_uri=oob" | |||
| end | |||
| def get_access_token(code) | |||
| uri = URI.parse(oauth_url(code)) | |||
| response = Net::HTTP.get_response(uri) | |||
| Rails.logger.info "baidu_tongji_auth response.body ===== #{response.body}" | |||
| if response.code.to_i == 200 | |||
| data = JSON.parse(response.body) | |||
| access_token = data['access_token'] | |||
| refresh_token = data['refresh_token'] | |||
| expires_in = data['expires_in'] | |||
| if access_token.present? | |||
| Rails.cache.write("baidu_tongji_auth/access_token", access_token, expires_in: expires_in) | |||
| Rails.cache.write("baidu_tongji_auth/refresh_token", refresh_token, expires_in: 1.year) | |||
| end | |||
| end | |||
| end | |||
| def refresh_access_token | |||
| url = "http://openapi.baidu.com/oauth/2.0/token?grant_type=refresh_token&refresh_token=#{refresh_token}&client_id=#{client_id}&client_secret=#{client_secret}" | |||
| uri = URI.parse(url) | |||
| response = Net::HTTP.get_response(uri) | |||
| Rails.logger.info "baidu_tongji_auth response.body ===== #{response.body}" | |||
| if response.code.to_i == 200 | |||
| data = JSON.parse(response.body) | |||
| access_token = data['access_token'] | |||
| refresh_token = data['refresh_token'] | |||
| expires_in = data['expires_in'] | |||
| if access_token.present? | |||
| Rails.cache.write("baidu_tongji_auth/access_token", access_token, expires_in: expires_in) | |||
| Rails.cache.write("baidu_tongji_auth/refresh_token", refresh_token, expires_in: 1.year) | |||
| end | |||
| end | |||
| end | |||
| def access_token | |||
| access_token = Rails.cache.read("baidu_tongji_auth/access_token") | |||
| if access_token.blank? && refresh_token.present? | |||
| refresh_access_token | |||
| access_token = Rails.cache.read("baidu_tongji_auth/access_token") | |||
| end | |||
| access_token | |||
| end | |||
| def refresh_token | |||
| refresh_token = Rails.cache.read("baidu_tongji_auth/refresh_token") | |||
| # 如果刷新token失效,access_token也重置 | |||
| if refresh_token.blank? | |||
| Rails.cache.delete("baidu_tongji_auth/access_token") | |||
| end | |||
| refresh_token | |||
| end | |||
| # 网站概况(趋势数据) | |||
| def api_overview | |||
| start_date = Time.now.beginning_of_week | |||
| end_date = Time.now | |||
| start_date = Time.now - 1.days if start_date.strftime("%Y%m%d") == end_date.strftime("%Y%m%d") | |||
| api("overview/getTimeTrendRpt", start_date, end_date, "pv_count,visitor_count,ip_count") | |||
| end | |||
| # 网站概况(来源网站、搜索词、入口页面、受访页面) | |||
| def api_overview_getCommonTrackRpt | |||
| start_date = Time.now.beginning_of_week | |||
| end_date = Time.now | |||
| api("overview/getCommonTrackRpt", start_date, end_date, "pv_count") | |||
| end | |||
| # 全部来源 | |||
| def source_from | |||
| start_date = Time.now.beginning_of_week | |||
| end_date = Time.now | |||
| api("source/all/a", start_date, end_date, "pv_count,visitor_count,ip_count") | |||
| end | |||
| def api(api_method, start_date, end_date, metrics = nil) | |||
| start_date_fmt = start_date.strftime("%Y%m%d") | |||
| end_date_fmt = end_date.strftime("%Y%m%d") | |||
| api_url = "https://openapi.baidu.com/rest/2.0/tongji/report/getData?access_token=#{access_token}&site_id=#{site_id}&method=#{api_method}&start_date=#{start_date_fmt}&end_date=#{end_date_fmt}&metrics=#{metrics}" | |||
| data = url_http_post(api_url, {}) | |||
| data['result'] | |||
| end | |||
| def url_http_post(api_url, params) | |||
| Rails.logger.info "api_url==#{api_url}" | |||
| uri = URI.parse(api_url) | |||
| http = Net::HTTP.new uri.host, uri.port | |||
| http.open_timeout = 60 | |||
| http.read_timeout = 60 | |||
| if uri.scheme == 'https' | |||
| http.verify_mode = OpenSSL::SSL::VERIFY_NONE | |||
| http.use_ssl = true | |||
| end | |||
| begin | |||
| request = Net::HTTP::Post.new(uri) | |||
| request.set_form_data(params) if params.present? | |||
| request['Content-Type'] = 'application/json;charset=utf-8' | |||
| # request['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8' | |||
| response = http.start { |http| http.request(request) } | |||
| Rails.logger.info "api response.body==#{response.body}" | |||
| JSON.parse response.body | |||
| rescue => err | |||
| Rails.logger.error("#############api_url:#{api_url},error:#{err.message.size}") | |||
| # Rails.logger.error("#############api_url:#{api_url},error:#{err.message}") | |||
| return {} | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -1,5 +1,5 @@ | |||
| class Cache::V2::ProjectCommonService < ApplicationService | |||
| attr_reader :project_id, :owner_id, :name, :identifier, :description, :visits, :watchers, :praises, :forks, :issues, :pullrequests, :commits | |||
| attr_reader :project_id, :owner_id, :name, :identifier, :description, :visits, :watchers, :praises, :forks, :issues, :closed_issues, :pullrequests, :commits | |||
| attr_accessor :project | |||
| def initialize(project_id, params={}) | |||
| @@ -13,6 +13,7 @@ class Cache::V2::ProjectCommonService < ApplicationService | |||
| @praises = params[:praises] | |||
| @forks = params[:forks] | |||
| @issues = params[:issues] | |||
| @closed_issues = params[:closed_issues] | |||
| @pullrequests = params[:pullrequests] | |||
| @commits = params[:commits] | |||
| end | |||
| @@ -78,6 +79,10 @@ class Cache::V2::ProjectCommonService < ApplicationService | |||
| "issues" | |||
| end | |||
| def closed_issues_key | |||
| "closed_issues" | |||
| end | |||
| def pullrequests_key | |||
| "pullrequests" | |||
| end | |||
| @@ -151,6 +156,10 @@ class Cache::V2::ProjectCommonService < ApplicationService | |||
| Cache::V2::ProjectRankService.call(@project_id, {issues: @issues}) | |||
| Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {issues: @issues}) | |||
| end | |||
| if @closed_issues.present? | |||
| $redis_cache.hincrby(project_common_key, closed_issues_key, @closed_issues) | |||
| Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {closed_issues: @closed_issues}) | |||
| end | |||
| if @pullrequests.present? | |||
| $redis_cache.hincrby(project_common_key, pullrequests_key, @pullrequests) | |||
| Cache::V2::ProjectRankService.call(@project_id, {pullrequests: @pullrequests}) | |||
| @@ -202,6 +211,10 @@ class Cache::V2::ProjectCommonService < ApplicationService | |||
| $redis_cache.hset(project_common_key, issues_key, Issue.issue_issue.where(project_id: @project_id).count) | |||
| end | |||
| def reset_project_closed_issues | |||
| $redis_cache.hset(project_common_key, closed_issues_key, Issue.issue_issue.closed.where(project_id: @project_id).count) | |||
| end | |||
| def reset_project_pullrequests | |||
| $redis_cache.hset(project_common_key, pullrequests_key, PullRequest.where(project_id: @project_id).count) | |||
| end | |||
| @@ -224,6 +237,7 @@ class Cache::V2::ProjectCommonService < ApplicationService | |||
| reset_project_praises | |||
| reset_project_forks | |||
| reset_project_issues | |||
| reset_project_closed_issues | |||
| reset_project_pullrequests | |||
| reset_project_commits | |||
| @@ -1,6 +1,6 @@ | |||
| # 项目日活跃度计算存储 | |||
| class Cache::V2::ProjectDateRankService < ApplicationService | |||
| attr_reader :project_id, :rank_date, :visits, :praises, :forks, :issues, :pullrequests, :commits | |||
| attr_reader :project_id, :rank_date, :visits, :praises, :forks, :issues, :closed_issues, :pullrequests, :commits | |||
| attr_accessor :project_common | |||
| def initialize(project_id, rank_date=Date.today, params={}) | |||
| @@ -11,6 +11,7 @@ class Cache::V2::ProjectDateRankService < ApplicationService | |||
| @praises = params[:praises] | |||
| @forks = params[:forks] | |||
| @issues = params[:issues] | |||
| @closed_issues = params[:closed_issues] | |||
| @pullrequests = params[:pullrequests] | |||
| @commits = params[:commits] | |||
| end | |||
| @@ -57,6 +58,9 @@ class Cache::V2::ProjectDateRankService < ApplicationService | |||
| $redis_cache.zincrby(project_rank_key, @issues.to_i * 5, @project_id) | |||
| $redis_cache.hincrby(project_rank_statistic_key, "issues", @issues.to_i) | |||
| end | |||
| if @closed_issues.present? | |||
| $redis_cache.hincrby(project_rank_statistic_key, "closed_issues", @closed_issues.to_i) | |||
| end | |||
| if @pullrequests.present? | |||
| $redis_cache.zincrby(project_rank_key, @pullrequests.to_i * 10, @project_id) | |||
| $redis_cache.hincrby(project_rank_statistic_key, "pullrequests", @pullrequests.to_i) | |||
| @@ -16,6 +16,6 @@ class Getway::Cms::GetService < Getway::ClientService | |||
| end | |||
| def url | |||
| "/cms/doc/open/#{doc_id}".freeze | |||
| "/cms/doc/open/baseInfo/#{doc_id}".freeze | |||
| end | |||
| end | |||
| @@ -59,7 +59,11 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService | |||
| if @body[:new_branch].present? && (@body[:new_branch].include?('/') || @body[:new_branch].include?('\'') || @body[:new_branch].include?('^') || @body[:new_branch].include?('*')) | |||
| error("不合法的分支名称!") | |||
| else | |||
| error("#{filepath}文件已存在,不能重复创建!") | |||
| if json_parse!(body)["message"].present? && json_parse!(body)["message"].starts_with?("branch already exists") | |||
| error("#{@body[:new_branch]}分支已存在!") | |||
| else | |||
| error("#{filepath}文件已存在,不能重复创建!") | |||
| end | |||
| end | |||
| else | |||
| Rails.logger.error("Gitea api url==#{url},status:#{status},body=#{body}") | |||
| @@ -29,7 +29,7 @@ class Gitea::User::GenerateTokenService < Gitea::ClientService | |||
| end | |||
| def request_params | |||
| { name: "#{@username}-#{token_name}" } | |||
| { name: "#{@username}-#{token_name}", scopes: ["all"] } | |||
| end | |||
| def token_name | |||
| @@ -26,7 +26,7 @@ class PageService | |||
| Rails.logger.info "################### PageService close_site #{user_id} / #{identifier}" | |||
| user = User.find user_id | |||
| uri = if identifier.present? | |||
| URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/#{identifier}/") | |||
| URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/*") | |||
| else | |||
| URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/") | |||
| end | |||
| @@ -67,7 +67,7 @@ class Repositories::CreateService < ApplicationService | |||
| end | |||
| def repository_params | |||
| params.merge(project_id: project.id) | |||
| params.merge(project_id: project.id).except(:auto_init) | |||
| end | |||
| def gitea_repository_params | |||
| @@ -1,6 +1,6 @@ | |||
| <% | |||
| define_admin_breadcrumbs do | |||
| add_admin_breadcrumb('云上实验室', admins_laboratories_path) | |||
| add_admin_breadcrumb('导航栏配置', admins_laboratories_path) | |||
| add_admin_breadcrumb('轮播图') | |||
| end | |||
| %> | |||
| @@ -0,0 +1,68 @@ | |||
| <div> | |||
| 数据来源百度统计,本周 [<%= @current_week_statistic.first&.date %> / <%= @current_week_statistic.last&.date %>] | |||
| </div> | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="20%">日期</th> | |||
| <th width="15%">访问量</th> | |||
| <th width="15%">访客数</th> | |||
| <th width="15%">IP数</th> | |||
| <th width="15%">直接访问占比</th> | |||
| <th width="15%">外部链接占比</th> | |||
| <th width="15%">搜索引擎占比</th> | |||
| <th width="20%">自定义</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if @current_week_statistic.size ==1 && @pre_week_statistic.present? %> | |||
| <tr class=""> | |||
| <td>上周合计</td> | |||
| <td><%= @pre_week_statistic.map(&:pv).sum %></td> | |||
| <td><%= @pre_week_statistic.map(&:visitor).sum %></td> | |||
| <td><%= @pre_week_statistic.map(&:ip).sum %></td> | |||
| <td><%= (@pre_week_statistic.map(&:source_through).sum.to_f / 7).round(2) %>%</td> | |||
| <td><%= (@pre_week_statistic.map(&:source_link).sum.to_f / 7).round(2) %>%</td> | |||
| <td><%= (@pre_week_statistic.map(&:source_search).sum.to_f / 7).round(2) %>%</td> | |||
| <td><%= ((@pre_week_statistic.map(&:source_custom) - [nil]).sum.to_f / 7).round(2) %>%</td> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% @current_week_statistic.each_with_index do |week, index| %> | |||
| <tr class=""> | |||
| <td><%= week.date %> </td> | |||
| <td><%= week.pv %></td> | |||
| <td><%= week.visitor %></td> | |||
| <td><%= week.ip %></td> | |||
| <td><%= week.source_through %>%</td> | |||
| <td><%= week.source_link %>%</td> | |||
| <td><%= week.source_search %>%</td> | |||
| <td><%= week.source_custom.to_f %>%</td> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <tr class=""> | |||
| <% current_week_size = @current_week_statistic.size %> | |||
| <td><span style="color: #ff8200">本周合计</span></td> | |||
| <td><span style="color: #ff8200"><%= @current_week_statistic.map(&:pv).sum %></span></td> | |||
| <td><span style="color: #ff8200"><%= @current_week_statistic.map(&:visitor).sum %></span></td> | |||
| <td><span style="color: #ff8200"><%= @current_week_statistic.map(&:ip).sum %></span></td> | |||
| <td><span style="color: #ff8200"><%= (@current_week_statistic.map(&:source_through).sum.to_f / current_week_size).round(2) %>%</span></td> | |||
| <td><span style="color: #ff8200"><%= (@current_week_statistic.map(&:source_link).sum.to_f / current_week_size).round(2) %>%</span></td> | |||
| <td><span style="color: #ff8200"><%= (@current_week_statistic.map(&:source_search).sum.to_f / current_week_size).round(2) %>%</span></td> | |||
| <td><span style="color: #ff8200"><%= ((@current_week_statistic.map(&:source_custom) - [nil]).sum.to_f / current_week_size).round(2) %>%</span></td> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <% unless @access_token.present? && @overview_data.present? %> | |||
| <div> | |||
| <a href="/admins/baidu_tongji" target="_blank">1.百度统计需人工授权,点击去授权</a> | |||
| </div> | |||
| <div> | |||
| <a href="/admins/baidu_tongji_auth" onclick="this.href = '/admins/baidu_tongji_auth?code=' + document.getElementById('auth_code').value">2.获取百度统计授权码</a> | |||
| <input name="code" id="auth_code" style="width: 380px" placeholder="请输入1.百度统计返回的code后,点击2.获取百度统计Token"/> | |||
| </div> | |||
| <% end %> | |||
| @@ -0,0 +1,67 @@ | |||
| <% if @access_token.present? && @overview_data.present? %> | |||
| <div> | |||
| 数据来源百度统计,本周 <%= @overview_data['timeSpan'] %> | |||
| </div> | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="20%">日期</th> | |||
| <th width="15%">访问量</th> | |||
| <th width="15%">访客数</th> | |||
| <th width="15%">IP数</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% pv_count = [] %> | |||
| <% visitor_count = [] %> | |||
| <% ip_count = [] %> | |||
| <% @overview_data['items'][0].each_with_index do |date, index| %> | |||
| <% pv = @overview_data['items'][1][index][0] %> | |||
| <% visitor = @overview_data['items'][1][index][1] %> | |||
| <% ip = @overview_data['items'][1][index][2] %> | |||
| <tr class=""> | |||
| <td><%= date[0] %> </td> | |||
| <td><%= pv %></td> | |||
| <td><%= visitor %></td> | |||
| <td><%= ip %></td> | |||
| </td> | |||
| </tr> | |||
| <% pv_count.push(pv) %> | |||
| <% visitor_count.push(visitor) %> | |||
| <% ip_count.push(ip) %> | |||
| <% end %> | |||
| <tr class=""> | |||
| <td><span style="color: #ff8200">合计</span></td> | |||
| <td><span style="color: #ff8200"><%= pv_count %></span></td> | |||
| <td><span style="color: #ff8200"><%= visitor_count %></span></td> | |||
| <td><span style="color: #ff8200"><%= ip_count %></span></td> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="15%">直接访问占比</th> | |||
| <th width="15%">外部链接占比</th> | |||
| <th width="15%">搜索引擎占比</th> | |||
| <th width="20%">自定义</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr class=""> | |||
| <% @source_from_data['items'][1].each_with_index do |source, index| %> | |||
| <td><%= ((source[0].to_f / @source_from_data['sum'][0][0].to_f) * 100).round(2).to_s %>%</td> | |||
| <% end %> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <% else %> | |||
| <div> | |||
| <a href="/admins/baidu_tongji" target="_blank">1.百度统计需人工授权,点击去授权</a> | |||
| </div> | |||
| <div> | |||
| <a href="/admins/baidu_tongji_auth" onclick="this.href = '/admins/baidu_tongji_auth?code=' + document.getElementById('auth_code').value">2.获取百度统计授权码</a> | |||
| <input name="code" id="auth_code" style="width: 380px" placeholder="请输入1.百度统计返回的code后,点击2.获取百度统计Token"/> | |||
| </div> | |||
| <% end %> | |||
| @@ -2,6 +2,38 @@ | |||
| <% add_admin_breadcrumb('概览', admins_path) %> | |||
| <% end %> | |||
| <% cache "/admin/dashboards/#{Time.now.strftime('%Y-%m-%d')}", :expires_in => 1.days do %> | |||
| <div class="header bg-gradient-primary pb-8 pt-md-8"> | |||
| <div class="container-fluid"> | |||
| <div class="header-body"> | |||
| <!-- Card stats --> | |||
| <div class="row"> | |||
| <%@subject_name.each_with_index do |subject, index| %> | |||
| <div class="col-xl-3 col-lg-6" style="padding-bottom: 15px;"> | |||
| <div class="card card-stats mb-4 mb-xl-0"> | |||
| <div class="card-body"> | |||
| <div class="row"> | |||
| <div class="col"> | |||
| <h5 class="card-title text-uppercase11 text-muted mb-0"><%=subject %></h5> | |||
| <span class="h2 font-weight-bold mb-0"><%= @subject_data[index] %></span> | |||
| </div> | |||
| <div class="col-auto"> | |||
| <div class="icon icon-shape rounded-circle shadow"> | |||
| <i class="fa <%=@subject_icon[index] %>"></i> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <%end %> | |||
| <div class="box admin-list-container project-language-list-container"> | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| @@ -53,6 +85,7 @@ | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/dashboards/baidu_tongji' %> | |||
| </div> | |||
| <div id="project-language-modals"> | |||
| </div> | |||
| @@ -106,7 +106,7 @@ | |||
| </div> | |||
| </br> | |||
| <div class="form-row"> | |||
| <%= f.input :description, as: :text,label: '拒绝理由:(拒绝时请填写拒绝理由,可以为空)', wrapper_html: { class: 'col-md-12' }, input_html: { maxlength: 100, size: 40, class: 'col-md-11' , value: @identity_verification.description } %> | |||
| <%= f.input :description, as: :text,label: '拒绝理由:(拒绝时请填写拒绝理由,不可以为空)', wrapper_html: { class: 'col-md-12' }, input_html: { maxlength: 100, size: 40, class: 'col-md-11' , value: @identity_verification.description } %> | |||
| </div> | |||
| <div class="form-row mt-4"> | |||
| <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %> | |||
| @@ -0,0 +1,37 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('疑修排行榜', admins_path) %> | |||
| <% end %> | |||
| <div class="box search-form-container user-list-form"> | |||
| <%= form_tag(admins_issues_rank_index_path, method: :get, class: 'form-inline search-form flex-1', id: 'issue-rank-date-form') do %> | |||
| <div class="input-group"> | |||
| <span class="input-group-text">开始日期</span> | |||
| <input class="form-control datetimepicker" type="text" name="begin_date" value="<%= params[:begin_date] || (Date.yesterday - 7.days).to_s%>" aria-label="选择日期"> | |||
| </div> | |||
| <div class="input-group"> | |||
| <span class="input-group-text">截止日期</span> | |||
| <input class="form-control datetimepicker" type="text" name="end_date" value="<%= params[:end_date] || Date.yesterday.to_s%>" aria-label="选择日期"> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| <div class="box admin-list-container issue-rank-list-container"> | |||
| <%= render partial: 'admins/issues_rank/shared/data_list', locals: { statistics: @statistics } %> | |||
| </div> | |||
| <script> | |||
| $(".datetimepicker").on('change', function() { | |||
| $("#issue-rank-date-form").submit() | |||
| }); | |||
| $(".datetimepicker").on('change', function() { | |||
| $("#issue-rank-date-form").submit() | |||
| }); | |||
| $('.datetimepicker').datetimepicker({ | |||
| language: 'zh-CN', // 中文语言包 | |||
| autoclose: 1, // 选中日期后自动关闭 | |||
| format: 'yyyy-mm-dd', // 日期格式 | |||
| minView: "month", // 最小日期显示单元,这里最小显示月份界面,即可以选择到日 | |||
| todayBtn: 1, // 显示今天按钮 | |||
| todayHighlight: 1, // 显示今天高亮 | |||
| }); | |||
| </script> | |||
| @@ -0,0 +1 @@ | |||
| $('.issue-rank-list-container').html("<%= j( render partial: 'admins/issues_rank/shared/data_list', locals: { statistics: @statistics } ) %>"); | |||
| @@ -0,0 +1,26 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="20%">排名</th> | |||
| <th width="20%">项目</th> | |||
| <th width="20%">新增疑修数</th> | |||
| <th width="20%">关闭疑修数</th> | |||
| <th width="20%">当前疑修数量</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% statistics.each_with_index do |item, index| %> | |||
| <tr class=""> | |||
| <td><%= index + 1%></td> | |||
| <td> | |||
| <a target="_blank" href="<%= "/#{item&.project&.owner&.login}/#{item&.project&.identifier}"%>"> | |||
| <%= "#{item&.project&.owner&.real_name}/#{item&.project&.name}" %> | |||
| </a> | |||
| </td> | |||
| <td><%= item&.issues %></td> | |||
| <td><%= item&.closed_issues %></td> | |||
| <td><%= item&.project&.issues&.issue_issue.count %></td> | |||
| </tr> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| @@ -1,5 +1,5 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('云上实验室') %> | |||
| <% add_admin_breadcrumb('导航栏配置') %> | |||
| <% end %> | |||
| <div class="box search-form-container laboratory-list-form"> | |||
| @@ -2,7 +2,7 @@ | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title">新建云上实验室</h5> | |||
| <h5 class="modal-title">新建导航栏配置</h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| @@ -1,5 +1,5 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %> | |||
| <% add_admin_breadcrumb('导航栏配置', admins_laboratories_path) %> | |||
| <% add_admin_breadcrumb('单位定制') %> | |||
| <% end %> | |||
| @@ -5,70 +5,34 @@ | |||
| <div class="box search-form-container user-list-form"> | |||
| <%= form_tag(admins_projects_rank_index_path, method: :get, class: 'form-inline search-form flex-1', id: 'project-rank-date-form') do %> | |||
| <div class="form-group mr-2"> | |||
| <label for="status">日期:</label> | |||
| <% dates_array = (0..30).to_a.map { |item| [(Date.today-item.days).to_s, (Date.today-item.days).to_s] } %> | |||
| <%= select_tag(:date, options_for_select(dates_array, params[:date]), class:"form-control",id: "project-rank-date-select")%> | |||
| <div class="input-group"> | |||
| <span class="input-group-text">开始日期</span> | |||
| <input class="form-control datetimepicker" type="text" name="begin_date" value="<%= params[:begin_date] || (Date.yesterday - 7.days).to_s%>" aria-label="选择日期"> | |||
| </div> | |||
| <div class="input-group"> | |||
| <span class="input-group-text">截止日期</span> | |||
| <input class="form-control datetimepicker" type="text" name="end_date" value="<%= params[:end_date] || Date.yesterday.to_s%>" aria-label="选择日期"> | |||
| </div> | |||
| <% end %> | |||
| <%= link_to '导出', "/项目活跃度排行.xls", class: 'btn btn-primary mr-3' %> | |||
| </div> | |||
| <div class="box admin-list-container project-language-list-container"> | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="20%">排名</th> | |||
| <th width="30%">项目</th> | |||
| <th width="10%">得分</th> | |||
| <th width="10%">访问数</th> | |||
| <th width="10%">关注数</th> | |||
| <th width="10%">点赞数</th> | |||
| <th width="10%">fork数</th> | |||
| <th width="10%">疑修数</th> | |||
| <th width="10%">合并请求数</th> | |||
| <th width="10%">提交数</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% @date_rank.each_with_index do |item, index| %> | |||
| <tr class=""> | |||
| <td><%= index + 1%></td> | |||
| <% project_common = $redis_cache.hgetall("v2-project-common:#{item[0]}") %> | |||
| <% owner_common = $redis_cache.hgetall("v2-owner-common:#{project_common["owner_id"]}")%> | |||
| <td> | |||
| <a href="/<%= owner_common["login"] %>/<%= project_common["identifier"]%>"> | |||
| <%= "#{owner_common["name"]}/#{project_common["name"]}" %> | |||
| </a> | |||
| </td> | |||
| <td><%= item[1] %></td> | |||
| <% project_date_statistic_key = "v2-project-statistic:#{item[0]}-#{@rank_date}"%> | |||
| <% if $redis_cache.exists(project_date_statistic_key)%> | |||
| <% visits = $redis_cache.hget(project_date_statistic_key, "visits") %> | |||
| <td><%= visits || 0 %></td> | |||
| <% watchers = $redis_cache.hget(project_date_statistic_key, "watchers") %> | |||
| <td><%= watchers || 0 %></td> | |||
| <% praises = $redis_cache.hget(project_date_statistic_key, "praises") %> | |||
| <td><%= praises || 0 %></td> | |||
| <% forks = $redis_cache.hget(project_date_statistic_key, "forks") %> | |||
| <td><%= forks || 0 %></td> | |||
| <% issues = $redis_cache.hget(project_date_statistic_key, "issues") %> | |||
| <td><%= issues || 0 %></td> | |||
| <% pullrequests = $redis_cache.hget(project_date_statistic_key, "pullrequests") %> | |||
| <td><%= pullrequests || 0 %></td> | |||
| <% commits = $redis_cache.hget(project_date_statistic_key, "commits") %> | |||
| <td><%= commits || 0 %></td> | |||
| <% else %> | |||
| <td colspan="7">暂无数据</td> | |||
| <% end %> | |||
| </tr> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <div class="box admin-list-container project-rank-list-container"> | |||
| <%= render partial: 'admins/projects_rank/shared/data_list', locals: { statistics: @statistics } %> | |||
| </div> | |||
| <script> | |||
| $("#project-rank-date-select").on('change', function() { | |||
| $(".datetimepicker").on('change', function() { | |||
| $("#project-rank-date-form").submit() | |||
| }); | |||
| $(".datetimepicker").on('change', function() { | |||
| $("#project-rank-date-form").submit() | |||
| }); | |||
| $('.datetimepicker').datetimepicker({ | |||
| language: 'zh-CN', // 中文语言包 | |||
| autoclose: 1, // 选中日期后自动关闭 | |||
| format: 'yyyy-mm-dd', // 日期格式 | |||
| minView: "month", // 最小日期显示单元,这里最小显示月份界面,即可以选择到日 | |||
| todayBtn: 1, // 显示今天按钮 | |||
| todayHighlight: 1, // 显示今天高亮 | |||
| }); | |||
| </script> | |||
| @@ -0,0 +1 @@ | |||
| $('.project-rank-list-container').html("<%= j( render partial: 'admins/projects_rank/shared/data_list', locals: { statistics: @statistics } ) %>"); | |||
| @@ -0,0 +1,37 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="20%">排名</th> | |||
| <th width="30%">项目</th> | |||
| <th width="10%">得分</th> | |||
| <th width="10%"><%= sort_tag('访问数', name: 'visits', path: admins_projects_rank_index_path) %></th> | |||
| <th width="10%"><%= sort_tag('关注数', name: 'watchers', path: admins_projects_rank_index_path) %></th> | |||
| <th width="10%"><%= sort_tag('点赞数', name: 'praises', path: admins_projects_rank_index_path) %></th> | |||
| <th width="10%"><%= sort_tag('fork数', name: 'forks', path: admins_projects_rank_index_path) %></th> | |||
| <th width="10%"><%= sort_tag('疑修数', name: 'issues', path: admins_projects_rank_index_path) %></th> | |||
| <th width="10%"><%= sort_tag('合并请求数', name: 'pullrequests', path: admins_projects_rank_index_path) %></th> | |||
| <th width="10%"><%= sort_tag('提交数', name: 'commits', path: admins_projects_rank_index_path) %></th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% statistics.each_with_index do |item, index| %> | |||
| <tr class=""> | |||
| <td><%= index + 1%></td> | |||
| <td> | |||
| <a target="_blank" href="<%= "/#{item&.project&.owner&.login}/#{item&.project&.identifier}"%>"> | |||
| <%= "#{item&.project&.owner&.real_name}/#{item&.project&.name}" %> | |||
| </a> | |||
| </td> | |||
| <td><%= item&.score %></td> | |||
| <td><%= item&.visits %></td> | |||
| <td><%= item&.watchers %></td> | |||
| <td><%= item&.praises %></td> | |||
| <td><%= item&.forks %></td> | |||
| <td><%= item&.issues %></td> | |||
| <td><%= item&.pullrequests %></td> | |||
| <td><%= item&.commits %></td> | |||
| </tr> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| @@ -11,13 +11,13 @@ | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 系统保留关键词标识 <span class="ml10 color-orange mr20">*</span> | |||
| 禁用词标识 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :identifier,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 系统保留关键词描述 | |||
| 禁用词描述 | |||
| </label> | |||
| <%= p.text_area :description,class: "form-control",placeholder: ""%> | |||
| </div> | |||
| @@ -1,5 +1,5 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('系统保留关键词') %> | |||
| <% add_admin_breadcrumb('禁用词管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| @@ -13,13 +13,7 @@ | |||
| <!-- Sidebar Links --> | |||
| <ul class="list-unstyled components"> | |||
| <li><%= sidebar_item(admins_path, '概览', icon: 'dashboard', controller: 'admins-dashboards') %></li> | |||
| <li> | |||
| <%= sidebar_item_group('#rank-submenu', '排行榜', icon: 'calendar') do %> | |||
| <li><%= sidebar_item(admins_users_rank_index_path, '用户排行榜', icon: 'user', controller: 'admins-users_rank') %></li> | |||
| <li><%= sidebar_item(admins_projects_rank_index_path, '项目排行榜', icon: 'database', controller: 'admins-projects_rank') %></li> | |||
| <% end %> | |||
| </li> | |||
| <li><%= sidebar_item(admins_path, '数据概览', icon: 'dashboard', controller: 'admins-dashboards') %></li> | |||
| <li> | |||
| <%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %> | |||
| <li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li> | |||
| @@ -27,15 +21,23 @@ | |||
| <% end %> | |||
| </li> | |||
| <li> | |||
| <%= sidebar_item_group('#pages-submenu', '站点管理', icon: 'cogs') do %> | |||
| <li><%= sidebar_item(admins_identity_verifications_path, '身份审核列表', icon: 'user', controller: 'admins-identity_verifications') %></li> | |||
| <li><%= sidebar_item(admins_site_pages_path, '用户站点列表', icon: 'sitemap', controller: 'admins-site_pages') %></li> | |||
| <li><%= sidebar_item(admins_page_themes_path, '站点主题配置', icon: 'cogs', controller: 'admins-page_themes') %></li> | |||
| <%= sidebar_item_group('#setting-submenu', '用户支持', icon: 'cogs') do %> | |||
| <li><%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs') %></li> | |||
| <li><%= sidebar_item(admins_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps') %></li> | |||
| <li><%= sidebar_item(admins_feedbacks_path, '用户反馈', icon: 'question-circle', controller: 'admins-feedbacks') %></li> | |||
| <li><%= sidebar_item(admins_system_notifications_path, '系统公告配置', icon: 'bell', controller: 'admins-system_notifications') %></li> | |||
| <% end %> | |||
| </li> | |||
| <li><%= sidebar_item(admins_laboratories_path, '导航栏配置', icon: 'cloud', controller: 'admins-laboratories') %></li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-system', '开发者配置', icon: 'wrench') do %> | |||
| <li><%= sidebar_item(admins_sites_path, 'setting接口配置', icon: 'deaf', controller: 'admins-sites') %></li> | |||
| <li><%= sidebar_item(admins_edu_settings_path, '全局变量配置', icon: 'pencil-square', controller: 'admins-edu_settings') %></li> | |||
| <li><%= sidebar_item(admins_message_templates_path, '消息模版配置', icon: 'folder', controller: 'admins-message_templates') %></li> | |||
| <% end %> | |||
| </li> | |||
| <li><%= sidebar_item(admins_reversed_keywords_path, '禁用词管理', icon: 'key', controller: 'admins-reversed_keywords') %></li> | |||
| <li> | |||
| <%= sidebar_item_group('#projects-submenu', '开源项目', icon: 'database') do %> | |||
| <li><%= sidebar_item(admins_projects_path, '项目列表', icon: 'database', controller: 'admins-projects') %></li> | |||
| @@ -45,9 +47,6 @@ | |||
| <li><%= sidebar_item(admins_project_ignores_path, '忽略文件', icon: 'git', controller: 'admins-project_ignores') %></li> | |||
| <% end %> | |||
| </li> | |||
| <li><%= sidebar_item(admins_reversed_keywords_path, '系统保留关键词', icon: 'key', controller: 'admins-reversed_keywords') %></li> | |||
| <li><%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %></li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-index', '首页配置', icon: 'file') do %> | |||
| <li><%= sidebar_item(admins_topic_banners_path, 'banner管理', icon: 'image', controller: 'admins-topic-banners') %></li> | |||
| @@ -71,25 +70,27 @@ | |||
| <% end %> | |||
| </li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-submenu', '网站建设', icon: 'cogs') do %> | |||
| <li><%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs') %></li> | |||
| <li><%= sidebar_item(admins_nps_path, 'NPS用户调研', icon: 'question-circle', controller: 'admins-nps') %></li> | |||
| <li><%= sidebar_item(admins_feedbacks_path, '用户反馈', icon: 'question-circle', controller: 'admins-feedbacks') %></li> | |||
| <%= sidebar_item_group('#pages-submenu', '个人站点管理', icon: 'cogs') do %> | |||
| <li><%= sidebar_item(admins_identity_verifications_path, '身份审核列表', icon: 'user', controller: 'admins-identity_verifications') %></li> | |||
| <li><%= sidebar_item(admins_site_pages_path, '用户站点列表', icon: 'sitemap', controller: 'admins-site_pages') %></li> | |||
| <li><%= sidebar_item(admins_page_themes_path, '站点主题配置', icon: 'cogs', controller: 'admins-page_themes') %></li> | |||
| <% end %> | |||
| </li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-system', '系统配置', icon: 'wrench') do %> | |||
| <li><%= sidebar_item(admins_sites_path, 'setting接口配置', icon: 'deaf', controller: 'admins-sites') %></li> | |||
| <li><%= sidebar_item(admins_edu_settings_path, '全局变量配置', icon: 'pencil-square', controller: 'admins-edu_settings') %></li> | |||
| <li><%= sidebar_item(admins_system_notifications_path, '系统通知配置', icon: 'bell', controller: 'admins-system_notifications') %></li> | |||
| <li><%= sidebar_item(admins_message_templates_path, '消息模版配置', icon: 'folder', controller: 'admins-message_templates') %></li> | |||
| <%= sidebar_item_group('#rank-submenu', '活跃度排行', icon: 'calendar') do %> | |||
| <li><%= sidebar_item(admins_users_rank_index_path, '用户活跃度排行', icon: 'user', controller: 'admins-users_rank') %></li> | |||
| <li><%= sidebar_item(admins_projects_rank_index_path, '项目活跃度排行', icon: 'database', controller: 'admins-projects_rank') %></li> | |||
| <li><%= sidebar_item(admins_issues_rank_index_path, '疑修活跃度排行', icon: 'calendar', controller: 'admins-issues_rank') %></li> | |||
| <% end %> | |||
| </li> | |||
| <%= render_admin_statistics_item %> | |||
| <li> | |||
| <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> | |||
| </li> | |||
| <%= render_admin_statistics_item %> | |||
| <li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li> | |||
| </ul> | |||