| @@ -10,7 +10,7 @@ | |||||
| # Ignore lock config file | # Ignore lock config file | ||||
| *.log | *.log | ||||
| .env | |||||
| # mac | # mac | ||||
| *.DS_Store | *.DS_Store | ||||
| .bashrc | .bashrc | ||||
| @@ -84,4 +84,5 @@ redis_data/ | |||||
| dump.rdb | dump.rdb | ||||
| .tags* | .tags* | ||||
| ceshi_user.xlsx | ceshi_user.xlsx | ||||
| public/trace_task_results | |||||
| public/trace_task_results | |||||
| public/项目活跃度排行.xls | |||||
| @@ -28,6 +28,41 @@ class Admins::DashboardsController < Admins::BaseController | |||||
| @day_new_project_count = Project.where(created_on: today).count | @day_new_project_count = Project.where(created_on: today).count | ||||
| @weekly_new_project_count = Project.where(created_on: current_week).count | @weekly_new_project_count = Project.where(created_on: current_week).count | ||||
| @month_new_project_count = Project.where(created_on: current_month).count | @month_new_project_count = Project.where(created_on: current_month).count | ||||
| # 总的平台用户数 | |||||
| # 总的平台项目数 | |||||
| # 总的平台组织数 | |||||
| # 总的平台Issue数、评论数、PR数、Commit数 | |||||
| @user_count = User.count | |||||
| @project_count = Project.count | |||||
| @organization_count = Organization.count | |||||
| @issue_count = Issue.count | |||||
| @comment_count = Journal.count | |||||
| @pr_count = PullRequest.count | |||||
| @commit_count = CommitLog.count | |||||
| @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 = tongji_service.overview_batch_add(start_date, end_date) | |||||
| tongji_service.source_from_batch_add(start_date, end_date) | |||||
| end | |||||
| @current_week_statistic = DailyPlatformStatistic.where(date: current_week) | |||||
| @pre_week_statistic = DailyPlatformStatistic.where(date: pre_week) | |||||
| end | end | ||||
| def month_active_user | def month_active_user | ||||
| @@ -42,6 +77,19 @@ class Admins::DashboardsController < Admins::BaseController | |||||
| render_ok(data: data) | render_ok(data: data) | ||||
| end | 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 | def evaluate | ||||
| names = [] | names = [] | ||||
| data = [] | data = [] | ||||
| @@ -63,8 +111,12 @@ class Admins::DashboardsController < Admins::BaseController | |||||
| Time.now.beginning_of_day..Time.now.end_of_day | Time.now.beginning_of_day..Time.now.end_of_day | ||||
| end | end | ||||
| def current_week | |||||
| def pre_7_days | |||||
| 7.days.ago.end_of_day..Time.now.end_of_day | 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 | end | ||||
| def current_month | def current_month | ||||
| @@ -72,6 +124,7 @@ class Admins::DashboardsController < Admins::BaseController | |||||
| end | end | ||||
| def pre_week | 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 | ||||
| end | end | ||||
| @@ -15,6 +15,7 @@ class Admins::IdentityVerificationsController < Admins::BaseController | |||||
| def update | def update | ||||
| if @identity_verification.update(update_params) | if @identity_verification.update(update_params) | ||||
| 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) | |||||
| redirect_to admins_identity_verifications_path | redirect_to admins_identity_verifications_path | ||||
| flash[:success] = "更新成功" | flash[:success] = "更新成功" | ||||
| else | else | ||||
| @@ -1,16 +1,55 @@ | |||||
| class Admins::ProjectsRankController < Admins::BaseController | class Admins::ProjectsRankController < Admins::BaseController | ||||
| def index | 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 | end | ||||
| private | private | ||||
| def rank_date | |||||
| params.fetch(:date, Date.today.to_s) | |||||
| def begin_date | |||||
| params.fetch(:begin_date, (Date.today-7.days).to_s) | |||||
| end | |||||
| def end_date | |||||
| params.fetch(:end_date, Date.today.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 | ||||
| end | end | ||||
| @@ -29,8 +29,12 @@ class Admins::SitePagesController < Admins::BaseController | |||||
| end | end | ||||
| def update | 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' | render 'edit' | ||||
| end | end | ||||
| @@ -25,7 +25,7 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||||
| @notification = SystemNotification.new(notification_params) | @notification = SystemNotification.new(notification_params) | ||||
| if @notification.save | if @notification.save | ||||
| redirect_to admins_system_notifications_path | redirect_to admins_system_notifications_path | ||||
| flash[:success] = '系统消息创建成功' | |||||
| flash[:success] = '系统公告创建成功' | |||||
| else | else | ||||
| redirect_to admins_system_notifications_path | redirect_to admins_system_notifications_path | ||||
| flash[:danger] = @notification.errors.full_messages.join(",") | flash[:danger] = @notification.errors.full_messages.join(",") | ||||
| @@ -37,7 +37,7 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||||
| if @notification.update_attributes(notification_params) | if @notification.update_attributes(notification_params) | ||||
| format.html do | format.html do | ||||
| redirect_to admins_system_notifications_path | redirect_to admins_system_notifications_path | ||||
| flash[:success] = '系统消息更新成功' | |||||
| flash[:success] = '系统公告更新成功' | |||||
| end | end | ||||
| format.js {render_ok} | format.js {render_ok} | ||||
| else | else | ||||
| @@ -53,10 +53,10 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||||
| def destroy | def destroy | ||||
| if @notification.destroy | if @notification.destroy | ||||
| redirect_to admins_system_notifications_path | redirect_to admins_system_notifications_path | ||||
| flash[:success] = "系统消息删除成功" | |||||
| flash[:success] = "系统公告删除成功" | |||||
| else | else | ||||
| redirect_to admins_system_notifications_path | redirect_to admins_system_notifications_path | ||||
| flash[:danger] = "系统消息删除失败" | |||||
| flash[:danger] = "系统公告删除失败" | |||||
| end | end | ||||
| end | end | ||||
| @@ -17,23 +17,31 @@ class SitePagesController < ApplicationController | |||||
| end | end | ||||
| def create | 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 = Page.new(create_params) | ||||
| @page.user = current_user | @page.user = current_user | ||||
| @page.project = @project | @page.project = @project | ||||
| @page.save | @page.save | ||||
| end | 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 | 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] | @page = Page.find params[:id] | ||||
| return normal_status(-1, @page.state_description) unless @page.state | return normal_status(-1, @page.state_description) unless @page.state | ||||
| response_str = @page.deploy_page(params[:branch]) | 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) | @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) | ||||
| else | else | ||||
| @page.update(build_state:false, last_build_info: data) | @page.update(build_state:false, last_build_info: data) | ||||
| @@ -42,22 +50,22 @@ class SitePagesController < ApplicationController | |||||
| end | end | ||||
| def softbot_build | def softbot_build | ||||
| branch = params[:ref].split("/").last | |||||
| branch = params[:ref].split('/').last | |||||
| user = User.find_by_login params[:repository][:owner][:login] | 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) | 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) | @page = Page.find_by(user: user, project: project) | ||||
| response_str = @page.deploy_page(branch) | response_str = @page.deploy_page(branch) | ||||
| data = JSON.parse(response_str)["result"] | |||||
| data = JSON.parse(response_str)['result'] | |||||
| if data.nil? | if data.nil? | ||||
| data = JSON.parse(response_str)["error"] | |||||
| data = JSON.parse(response_str)['error'] | |||||
| end | end | ||||
| if data.include?("部署成功") | |||||
| if data.include?('部署成功') | |||||
| @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) | @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) | ||||
| else | else | ||||
| @page.update(build_state:false, last_build_info: data) | @page.update(build_state:false, last_build_info: data) | ||||
| @@ -85,7 +93,7 @@ class SitePagesController < ApplicationController | |||||
| end | end | ||||
| def theme_params | def theme_params | ||||
| params[:language_frame] || "hugo" | |||||
| params[:language_frame] || 'hugo' | |||||
| end | end | ||||
| def create_params | def create_params | ||||
| @@ -1,12 +1,12 @@ | |||||
| class Organizations::CreateForm < BaseForm | 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 | 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, :visibility, presence: true | ||||
| validates :name, :nickname, length: { maximum: 100 } | validates :name, :nickname, length: { maximum: 100 } | ||||
| validates :location, length: { maximum: 50 } | validates :location, length: { maximum: 50 } | ||||
| validates :description, length: { maximum: 200 } | 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 | validate do | ||||
| check_name(name) unless name.blank? || name == original_name | check_name(name) unless name.blank? || name == original_name | ||||
| @@ -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,33 @@ | |||||
| 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 | |||||
| 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, | |||||
| pullrequests: pullrequests, | |||||
| commits: commits | |||||
| ) | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -1,44 +1,45 @@ | |||||
| # == Schema Information | |||||
| # | |||||
| # Table name: attachments | |||||
| # | |||||
| # id :integer not null, primary key | |||||
| # container_id :integer | |||||
| # container_type :string(30) | |||||
| # filename :string(255) default(""), not null | |||||
| # disk_filename :string(255) default(""), not null | |||||
| # filesize :integer default("0"), not null | |||||
| # content_type :string(255) default("") | |||||
| # digest :string(60) default(""), not null | |||||
| # downloads :integer default("0"), not null | |||||
| # author_id :integer default("0"), not null | |||||
| # created_on :datetime | |||||
| # description :text(65535) | |||||
| # disk_directory :string(255) | |||||
| # attachtype :integer default("1") | |||||
| # is_public :integer default("1") | |||||
| # copy_from :integer | |||||
| # quotes :integer default("0") | |||||
| # is_publish :integer default("1") | |||||
| # publish_time :datetime | |||||
| # resource_bank_id :integer | |||||
| # unified_setting :boolean default("1") | |||||
| # cloud_url :string(255) default("") | |||||
| # course_second_category_id :integer default("0") | |||||
| # delay_publish :boolean default("0") | |||||
| # memo_image :boolean default("0") | |||||
| # extra_type :integer default("0") | |||||
| # uuid :string(255) | |||||
| # | |||||
| # Indexes | |||||
| # | |||||
| # index_attachments_on_author_id (author_id) | |||||
| # index_attachments_on_container_id_and_container_type (container_id,container_type) | |||||
| # index_attachments_on_course_second_category_id (course_second_category_id) | |||||
| # index_attachments_on_created_on (created_on) | |||||
| # index_attachments_on_is_public (is_public) | |||||
| # index_attachments_on_quotes (quotes) | |||||
| # | |||||
| # == Schema Information | |||||
| # | |||||
| # Table name: attachments | |||||
| # | |||||
| # id :integer not null, primary key | |||||
| # container_id :integer | |||||
| # container_type :string(30) | |||||
| # filename :string(255) default(""), not null | |||||
| # disk_filename :string(255) default(""), not null | |||||
| # filesize :integer default("0"), not null | |||||
| # content_type :string(255) default("") | |||||
| # digest :string(60) default(""), not null | |||||
| # downloads :integer default("0"), not null | |||||
| # author_id :integer default("0"), not null | |||||
| # created_on :datetime | |||||
| # description :text(65535) | |||||
| # disk_directory :string(255) | |||||
| # attachtype :integer default("1") | |||||
| # is_public :integer default("1") | |||||
| # copy_from :integer | |||||
| # quotes :integer default("0") | |||||
| # is_publish :integer default("1") | |||||
| # publish_time :datetime | |||||
| # resource_bank_id :integer | |||||
| # unified_setting :boolean default("1") | |||||
| # cloud_url :string(255) default("") | |||||
| # course_second_category_id :integer default("0") | |||||
| # delay_publish :boolean default("0") | |||||
| # memo_image :boolean default("0") | |||||
| # extra_type :integer default("0") | |||||
| # uuid :string(255) | |||||
| # | |||||
| # Indexes | |||||
| # | |||||
| # index_attachments_on_author_id (author_id) | |||||
| # index_attachments_on_container_id_and_container_type (container_id,container_type) | |||||
| # index_attachments_on_course_second_category_id (course_second_category_id) | |||||
| # index_attachments_on_created_on (created_on) | |||||
| # index_attachments_on_is_public (is_public) | |||||
| # index_attachments_on_quotes (quotes) | |||||
| # | |||||
| @@ -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,28 @@ | |||||
| # == Schema Information | |||||
| # | |||||
| # Table name: daily_project_statistics | |||||
| # | |||||
| # id :integer not null, primary key | |||||
| # project_id :integer | |||||
| # date :string(255) | |||||
| # 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 | |||||
| # | |||||
| # Indexes | |||||
| # | |||||
| # index_daily_project_statistics_on_date (date) | |||||
| # index_daily_project_statistics_on_project_id (project_id) | |||||
| # | |||||
| class DailyProjectStatistic < ApplicationRecord | |||||
| belongs_to :project | |||||
| end | |||||
| @@ -36,12 +36,17 @@ | |||||
| # blockchain_token_num :integer | # blockchain_token_num :integer | ||||
| # pm_project_id :integer | # pm_project_id :integer | ||||
| # pm_sprint_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 | # Indexes | ||||
| # | # | ||||
| # index_issues_on_assigned_to_id (assigned_to_id) | # index_issues_on_assigned_to_id (assigned_to_id) | ||||
| # index_issues_on_author_id (author_id) | # index_issues_on_author_id (author_id) | ||||
| # index_issues_on_category_id (category_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_created_on (created_on) | ||||
| # index_issues_on_fixed_version_id (fixed_version_id) | # index_issues_on_fixed_version_id (fixed_version_id) | ||||
| # index_issues_on_priority_id (priority_id) | # index_issues_on_priority_id (priority_id) | ||||
| @@ -14,6 +14,7 @@ | |||||
| # gid :integer | # gid :integer | ||||
| # gitea_url :string(255) | # gitea_url :string(255) | ||||
| # pull_requests_count :integer default("0") | # pull_requests_count :integer default("0") | ||||
| # pm_project_id :integer | |||||
| # | # | ||||
| # Indexes | # Indexes | ||||
| # | # | ||||
| @@ -27,6 +27,7 @@ | |||||
| # | # | ||||
| # index_journals_on_created_on (created_on) | # index_journals_on_created_on (created_on) | ||||
| # index_journals_on_journalized_id (journalized_id) | # index_journals_on_journalized_id (journalized_id) | ||||
| # index_journals_on_parent_id (parent_id) | |||||
| # index_journals_on_review_id (review_id) | # index_journals_on_review_id (review_id) | ||||
| # index_journals_on_user_id (user_id) | # index_journals_on_user_id (user_id) | ||||
| # journals_journalized_id (journalized_id,journalized_type) | # journals_journalized_id (journalized_id,journalized_type) | ||||
| @@ -64,7 +64,7 @@ | |||||
| class Organization < Owner | class Organization < Owner | ||||
| alias_attribute :name, :login | 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") } | default_scope { where(type: "Organization") } | ||||
| @@ -79,7 +79,7 @@ class Organization < Owner | |||||
| validates :login, presence: true | validates :login, presence: true | ||||
| validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, case_sensitive: false | 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, | delegate :description, :website, :location, :repo_admin_change_team_access, :recommend, | ||||
| :visibility, :max_repo_creation, :num_projects, :num_users, :num_teams, | :visibility, :max_repo_creation, :num_projects, :num_users, :num_teams, | ||||
| @@ -34,6 +34,10 @@ class Page < ApplicationRecord | |||||
| PageService.genernate_user(user_id) | PageService.genernate_user(user_id) | ||||
| end | end | ||||
| before_destroy do | |||||
| PageService.close_site(user_id, identifier) | |||||
| end | |||||
| before_save do | before_save do | ||||
| if state_changed? && state == false | if state_changed? && state == false | ||||
| PageService.close_site(user_id, identifier) | PageService.close_site(user_id, identifier) | ||||
| @@ -46,7 +50,7 @@ class Page < ApplicationRecord | |||||
| def url | def url | ||||
| @deploy_domain = EduSetting.find_by_name("site_page_deploy_domain").try(:value) | @deploy_domain = EduSetting.find_by_name("site_page_deploy_domain").try(:value) | ||||
| "http://#{user.login}.#{@deploy_domain}/#{identifier}" | |||||
| "http://#{identifier}" | |||||
| end | end | ||||
| def build_script_path | def build_script_path | ||||
| @@ -136,6 +136,7 @@ class Project < ApplicationRecord | |||||
| has_many :project_topic_ralates, dependent: :destroy | has_many :project_topic_ralates, dependent: :destroy | ||||
| has_many :project_topics, through: :project_topic_ralates | has_many :project_topics, through: :project_topic_ralates | ||||
| has_many :commit_logs, dependent: :destroy | has_many :commit_logs, dependent: :destroy | ||||
| has_many :daily_project_statistics, dependent: :destroy | |||||
| after_create :incre_user_statistic, :incre_platform_statistic | after_create :incre_user_statistic, :incre_platform_statistic | ||||
| after_save :check_project_members | after_save :check_project_members | ||||
| before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data | before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data | ||||
| @@ -189,7 +189,7 @@ class User < Owner | |||||
| has_many :user_clas, :dependent => :destroy | has_many :user_clas, :dependent => :destroy | ||||
| has_many :clas, through: :user_clas | has_many :clas, through: :user_clas | ||||
| has_many :pages, :dependent => :destroy | |||||
| has_one :page, :dependent => :destroy | |||||
| # Groups and active users | # Groups and active users | ||||
| scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } | scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } | ||||
| @@ -773,7 +773,7 @@ class User < Owner | |||||
| def check_website_permission | def check_website_permission | ||||
| if website_permission_changed? && website_permission == false | 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) | PageService.close_site(self.id) | ||||
| end | end | ||||
| end | end | ||||
| @@ -60,13 +60,31 @@ class Api::V1::Issues::ListService < ApplicationService | |||||
| issues = issues.where(author_id: author_id) if author_id.present? | issues = issues.where(author_id: author_id) if author_id.present? | ||||
| # issue_tag_ids | # 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 | # 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 | # 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 | # status_id | ||||
| issues = issues.where(status_id: status_id) if status_id.present? && category != 'closed' | issues = issues.where(status_id: status_id) if status_id.present? && category != 'closed' | ||||
| @@ -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_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 | |||||
| @@ -26,7 +26,7 @@ class PageService | |||||
| Rails.logger.info "################### PageService close_site #{user_id} / #{identifier}" | Rails.logger.info "################### PageService close_site #{user_id} / #{identifier}" | ||||
| user = User.find user_id | user = User.find user_id | ||||
| uri = if identifier.present? | 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 | else | ||||
| URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/") | URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/") | ||||
| end | end | ||||
| @@ -1,6 +1,6 @@ | |||||
| <% | <% | ||||
| define_admin_breadcrumbs do | define_admin_breadcrumbs do | ||||
| add_admin_breadcrumb('云上实验室', admins_laboratories_path) | |||||
| add_admin_breadcrumb('导航栏配置', admins_laboratories_path) | |||||
| add_admin_breadcrumb('轮播图') | add_admin_breadcrumb('轮播图') | ||||
| end | end | ||||
| %> | %> | ||||
| @@ -0,0 +1,50 @@ | |||||
| <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> | |||||
| <% @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="">--> | |||||
| <!-- <td><span style="color: #ff8200">合计</span></td>--> | |||||
| <!-- <td><span style="color: #ff8200"><%#= @current_week_statistic.sum(:pv) %></span></td>--> | |||||
| <!-- <td><span style="color: #ff8200"><%#= @current_week_statistic.sum(:visitor) %></span></td>--> | |||||
| <!-- <td><span style="color: #ff8200"><%#= @current_week_statistic.sum(:ip) %></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 %> | |||||
| @@ -1,6 +1,35 @@ | |||||
| <% define_admin_breadcrumbs do %> | <% define_admin_breadcrumbs do %> | ||||
| <% add_admin_breadcrumb('概览', admins_path) %> | <% add_admin_breadcrumb('概览', admins_path) %> | ||||
| <% end %> | <% end %> | ||||
| <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> | |||||
| <div class="box admin-list-container project-language-list-container"> | <div class="box admin-list-container project-language-list-container"> | ||||
| <table class="table table-hover text-center subject-list-table"> | <table class="table table-hover text-center subject-list-table"> | ||||
| @@ -53,6 +82,7 @@ | |||||
| </tr> | </tr> | ||||
| </tbody> | </tbody> | ||||
| </table> | </table> | ||||
| <%= render partial: 'admins/dashboards/baidu_tongji' %> | |||||
| </div> | </div> | ||||
| <div id="project-language-modals"> | <div id="project-language-modals"> | ||||
| </div> | </div> | ||||
| @@ -1,5 +1,5 @@ | |||||
| <% define_admin_breadcrumbs do %> | <% define_admin_breadcrumbs do %> | ||||
| <% add_admin_breadcrumb('云上实验室') %> | |||||
| <% add_admin_breadcrumb('导航栏配置') %> | |||||
| <% end %> | <% end %> | ||||
| <div class="box search-form-container laboratory-list-form"> | <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-dialog modal-dialog-centered" role="document"> | ||||
| <div class="modal-content"> | <div class="modal-content"> | ||||
| <div class="modal-header"> | <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"> | <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
| <span aria-hidden="true">×</span> | <span aria-hidden="true">×</span> | ||||
| </button> | </button> | ||||
| @@ -1,5 +1,5 @@ | |||||
| <% define_admin_breadcrumbs do %> | <% define_admin_breadcrumbs do %> | ||||
| <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %> | |||||
| <% add_admin_breadcrumb('导航栏配置', admins_laboratories_path) %> | |||||
| <% add_admin_breadcrumb('单位定制') %> | <% add_admin_breadcrumb('单位定制') %> | ||||
| <% end %> | <% end %> | ||||
| @@ -5,70 +5,34 @@ | |||||
| <div class="box search-form-container user-list-form"> | <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 %> | <%= 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.today - 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.today.to_s%>" aria-label="选择日期"> | |||||
| </div> | </div> | ||||
| <% end %> | <% end %> | ||||
| <%= link_to '导出', "/项目活跃度排行.xls", class: 'btn btn-primary mr-3' %> | |||||
| </div> | </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> | </div> | ||||
| <script> | <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() | $("#project-rank-date-form").submit() | ||||
| }); | }); | ||||
| $('.datetimepicker').datetimepicker({ | |||||
| language: 'zh-CN', // 中文语言包 | |||||
| autoclose: 1, // 选中日期后自动关闭 | |||||
| format: 'yyyy-mm-dd', // 日期格式 | |||||
| minView: "month", // 最小日期显示单元,这里最小显示月份界面,即可以选择到日 | |||||
| todayBtn: 1, // 显示今天按钮 | |||||
| todayHighlight: 1, // 显示今天高亮 | |||||
| }); | |||||
| </script> | </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="modal-body"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label> | <label> | ||||
| 系统保留关键词标识 <span class="ml10 color-orange mr20">*</span> | |||||
| 禁用词标识 <span class="ml10 color-orange mr20">*</span> | |||||
| </label> | </label> | ||||
| <%= p.text_field :identifier,class: "form-control input-lg",required: true%> | <%= p.text_field :identifier,class: "form-control input-lg",required: true%> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label> | <label> | ||||
| 系统保留关键词描述 | |||||
| 禁用词描述 | |||||
| </label> | </label> | ||||
| <%= p.text_area :description,class: "form-control",placeholder: ""%> | <%= p.text_area :description,class: "form-control",placeholder: ""%> | ||||
| </div> | </div> | ||||
| @@ -1,5 +1,5 @@ | |||||
| <% define_admin_breadcrumbs do %> | <% define_admin_breadcrumbs do %> | ||||
| <% add_admin_breadcrumb('系统保留关键词') %> | |||||
| <% add_admin_breadcrumb('禁用词管理') %> | |||||
| <% end %> | <% end %> | ||||
| <div class="box search-form-container project-list-form"> | <div class="box search-form-container project-list-form"> | ||||
| @@ -13,13 +13,7 @@ | |||||
| <!-- Sidebar Links --> | <!-- Sidebar Links --> | ||||
| <ul class="list-unstyled components"> | <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> | <li> | ||||
| <%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %> | <%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %> | ||||
| <li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li> | <li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li> | ||||
| @@ -27,15 +21,23 @@ | |||||
| <% end %> | <% end %> | ||||
| </li> | </li> | ||||
| <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 %> | <% end %> | ||||
| </li> | </li> | ||||
| <li><%= sidebar_item(admins_reversed_keywords_path, '禁用词管理', icon: 'key', controller: 'admins-reversed_keywords') %></li> | |||||
| <li> | <li> | ||||
| <%= sidebar_item_group('#projects-submenu', '开源项目', icon: 'database') do %> | <%= sidebar_item_group('#projects-submenu', '开源项目', icon: 'database') do %> | ||||
| <li><%= sidebar_item(admins_projects_path, '项目列表', icon: 'database', controller: 'admins-projects') %></li> | <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> | <li><%= sidebar_item(admins_project_ignores_path, '忽略文件', icon: 'git', controller: 'admins-project_ignores') %></li> | ||||
| <% end %> | <% end %> | ||||
| </li> | </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> | <li> | ||||
| <%= sidebar_item_group('#setting-index', '首页配置', icon: 'file') do %> | <%= sidebar_item_group('#setting-index', '首页配置', icon: 'file') do %> | ||||
| <li><%= sidebar_item(admins_topic_banners_path, 'banner管理', icon: 'image', controller: 'admins-topic-banners') %></li> | <li><%= sidebar_item(admins_topic_banners_path, 'banner管理', icon: 'image', controller: 'admins-topic-banners') %></li> | ||||
| @@ -71,25 +70,26 @@ | |||||
| <% end %> | <% end %> | ||||
| </li> | </li> | ||||
| <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 %> | <% end %> | ||||
| </li> | </li> | ||||
| <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> | |||||
| <% end %> | <% end %> | ||||
| </li> | </li> | ||||
| <%= render_admin_statistics_item %> | |||||
| <li> | <li> | ||||
| <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> | <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> | ||||
| </li> | </li> | ||||
| <%= render_admin_statistics_item %> | |||||
| <li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li> | <li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li> | ||||
| </ul> | </ul> | ||||
| @@ -1,5 +1,5 @@ | |||||
| <div class="box search-form-container project-list-form"> | <div class="box search-form-container project-list-form"> | ||||
| <div style="line-height: 38px;" class="flex-1"><%= type == "create" ? "新建" : "编辑" %>系统通知</div> | |||||
| <div style="line-height: 38px;" class="flex-1"><%= type == "create" ? "新建" : "编辑" %>系统公告</div> | |||||
| <%= link_to "返回", admins_system_notifications_path, class: "btn btn-default pull-right" %> | <%= link_to "返回", admins_system_notifications_path, class: "btn btn-default pull-right" %> | ||||
| </div> | </div> | ||||
| @@ -8,36 +8,36 @@ | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label> | <label> | ||||
| <span class="color-grey-6 pt10"> | <span class="color-grey-6 pt10"> | ||||
| 系统通知标题 | |||||
| 系统公告标题 | |||||
| <span class="ml10 color-orange mr20">*</span> | <span class="ml10 color-orange mr20">*</span> | ||||
| </span> | </span> | ||||
| </label> | </label> | ||||
| <div class="mt-10"> | <div class="mt-10"> | ||||
| <%= p.text_field :subject, class: "form-control input-lg", placeholder: "请输入系统通知标题" %> | |||||
| <%= p.text_field :subject, class: "form-control input-lg", placeholder: "请输入系统公告标题" %> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label> | <label> | ||||
| <span class="color-grey-6 pt10"> | <span class="color-grey-6 pt10"> | ||||
| 系统通知副标题 | |||||
| 系统公告副标题 | |||||
| <span class="ml10 color-orange mr20">*</span> | <span class="ml10 color-orange mr20">*</span> | ||||
| </span> | </span> | ||||
| </label> | </label> | ||||
| <div class="mt-10"> | <div class="mt-10"> | ||||
| <%= p.text_field :sub_subject, class: "form-control input-lg", placeholder: "请输入系统通知副标题" %> | |||||
| <%= p.text_field :sub_subject, class: "form-control input-lg", placeholder: "请输入系统公告副标题" %> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label> | <label> | ||||
| <span class="color-grey-6 pt10"> | <span class="color-grey-6 pt10"> | ||||
| 系统通知正文 | |||||
| 系统公告正文 | |||||
| <span class="ml10 color-orange mr20">*</span> | <span class="ml10 color-orange mr20">*</span> | ||||
| </span> | </span> | ||||
| </label> | </label> | ||||
| <div class="pl-0 my-3 setting-item-body" id="system-notification-content-editor"> | <div class="pl-0 my-3 setting-item-body" id="system-notification-content-editor"> | ||||
| <%= p.text_area :content, class:"form-control", style: 'display: none;', rows: "10", cols: "20", placeholer: "请输入系统通知正文" %> | |||||
| <%= p.text_area :content, class:"form-control", style: 'display: none;', rows: "10", cols: "20", placeholer: "请输入系统公告正文" %> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -1,11 +1,11 @@ | |||||
| <% define_admin_breadcrumbs do %> | <% define_admin_breadcrumbs do %> | ||||
| <% add_admin_breadcrumb('系统通知模版') %> | |||||
| <% add_admin_breadcrumb('系统公告配置') %> | |||||
| <% end %> | <% end %> | ||||
| <div id="admins-system-notification-content"> | <div id="admins-system-notification-content"> | ||||
| <div class="box search-form-container project-list-form"> | <div class="box search-form-container project-list-form"> | ||||
| <%= form_tag(admins_system_notifications_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> | <%= form_tag(admins_system_notifications_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> | ||||
| <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '系统通知标题检索') %> | |||||
| <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '系统公告标题检索') %> | |||||
| <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> | <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> | ||||
| <input type="reset" class="btn btn-secondary clear-btn" value="清空"/> | <input type="reset" class="btn btn-secondary clear-btn" value="清空"/> | ||||
| <% end %> | <% end %> | ||||
| @@ -34,7 +34,7 @@ | |||||
| <td><%= index + 1%></td> | <td><%= index + 1%></td> | ||||
| <% owner_common = $redis_cache.hgetall("v2-owner-common:#{item[0]}")%> | <% owner_common = $redis_cache.hgetall("v2-owner-common:#{item[0]}")%> | ||||
| <td> | <td> | ||||
| <a href="/<%= owner_common["login"] %>"> | |||||
| <a target="_blank" href="/<%= owner_common["login"] %>"> | |||||
| <%= owner_common["name"] %> | <%= owner_common["name"] %> | ||||
| </a> | </a> | ||||
| </td> | </td> | ||||
| @@ -19,6 +19,7 @@ json.version_releasesed_count @project.releases_size(@user.try(:id), "released") | |||||
| json.permission render_permission(@user, @project) | json.permission render_permission(@user, @project) | ||||
| json.mirror_url @project&.repository.remote_mirror_url | json.mirror_url @project&.repository.remote_mirror_url | ||||
| json.mirror @project&.repository.mirror_url.present? | json.mirror @project&.repository.mirror_url.present? | ||||
| json.web_site @project.page.try(:identifier) | |||||
| json.type @project.numerical_for_project_type | json.type @project.numerical_for_project_type | ||||
| json.open_devops @project.open_devops? | json.open_devops @project.open_devops? | ||||
| json.topics @project.project_topics.each do |topic| | json.topics @project.project_topics.each do |topic| | ||||
| @@ -795,6 +795,8 @@ Rails.application.routes.draw do | |||||
| namespace :admins do | namespace :admins do | ||||
| mount Sidekiq::Web => '/sidekiq' | mount Sidekiq::Web => '/sidekiq' | ||||
| get '/', to: 'dashboards#index' | get '/', to: 'dashboards#index' | ||||
| get '/baidu_tongji', to: 'dashboards#baidu_tongji' | |||||
| get '/baidu_tongji_auth', to: 'dashboards#baidu_tongji_auth' | |||||
| namespace :topic do | namespace :topic do | ||||
| resources :activity_forums | resources :activity_forums | ||||
| resources :banners | resources :banners | ||||
| @@ -7,3 +7,13 @@ delay_expired_issue: | |||||
| cron: "0 0 * * *" | cron: "0 0 * * *" | ||||
| class: "DelayExpiredIssueJob" | class: "DelayExpiredIssueJob" | ||||
| queue: message | queue: message | ||||
| create_daily_project_statistics: | |||||
| cron: "0 1 * * *" | |||||
| class: "DailyProjectStatisticsJob" | |||||
| queue: cache | |||||
| daily_platform_statistics: | |||||
| cron: "0 1 * * *" | |||||
| class: "DailyPlatformStatisticsJob" | |||||
| queue: default | |||||
| @@ -0,0 +1,19 @@ | |||||
| class CreateDailyProjectStatistics < ActiveRecord::Migration[5.2] | |||||
| def change | |||||
| create_table :daily_project_statistics do |t| | |||||
| t.references :project | |||||
| t.date :date | |||||
| t.index :date | |||||
| t.integer :score, default: 0 | |||||
| t.integer :visits, default: 0 | |||||
| t.integer :watchers, default: 0 | |||||
| t.integer :praises, default: 0 | |||||
| t.integer :forks, default: 0 | |||||
| t.integer :issues, default: 0 | |||||
| t.integer :pullrequests, default: 0 | |||||
| t.integer :commits, default: 0 | |||||
| t.timestamps | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -0,0 +1,18 @@ | |||||
| class CreateDailyPlatformStatistics < ActiveRecord::Migration[5.2] | |||||
| def change | |||||
| create_table :daily_platform_statistics do |t| | |||||
| t.date :date | |||||
| t.index :date, unique: true | |||||
| t.bigint :pv, default: 0 | |||||
| t.bigint :visitor, default: 0 | |||||
| t.bigint :ip, default: 0 | |||||
| t.float :weekly_keep_rate, default: 0 | |||||
| t.float :source_through, default: 0 | |||||
| t.float :source_link, default: 0 | |||||
| t.float :source_search, default: 0 | |||||
| t.float :source_custom, default: 0 | |||||
| t.timestamps | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -0,0 +1,5 @@ | |||||
| require 'rails_helper' | |||||
| RSpec.describe DailyPlatformStatistic, type: :model do | |||||
| pending "add some examples to (or delete) #{__FILE__}" | |||||
| end | |||||