diff --git a/.gitignore b/.gitignore index 4a01bd5ec..d0186c4a3 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file +public/trace_task_results +public/项目活跃度排行.xls \ No newline at end of file diff --git a/app/controllers/admins/dashboards_controller.rb b/app/controllers/admins/dashboards_controller.rb index 2c01c8bd0..1e13621e9 100644 --- a/app/controllers/admins/dashboards_controller.rb +++ b/app/controllers/admins/dashboards_controller.rb @@ -28,6 +28,41 @@ class Admins::DashboardsController < Admins::BaseController @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 + + # 总的平台用户数 + # 总的平台项目数 + # 总的平台组织数 + # 总的平台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 def month_active_user @@ -42,6 +77,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 +111,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 +124,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 \ No newline at end of file diff --git a/app/controllers/admins/identity_verifications_controller.rb b/app/controllers/admins/identity_verifications_controller.rb index 51d5e423c..26dc35d3f 100644 --- a/app/controllers/admins/identity_verifications_controller.rb +++ b/app/controllers/admins/identity_verifications_controller.rb @@ -15,6 +15,7 @@ class Admins::IdentityVerificationsController < Admins::BaseController def update 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 flash[:success] = "更新成功" else diff --git a/app/controllers/admins/projects_rank_controller.rb b/app/controllers/admins/projects_rank_controller.rb index c4f968da4..e4d8046e0 100644 --- a/app/controllers/admins/projects_rank_controller.rb +++ b/app/controllers/admins/projects_rank_controller.rb @@ -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.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 \ No newline at end of file diff --git a/app/controllers/admins/site_pages_controller.rb b/app/controllers/admins/site_pages_controller.rb index f0e05e71d..306c91627 100644 --- a/app/controllers/admins/site_pages_controller.rb +++ b/app/controllers/admins/site_pages_controller.rb @@ -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 diff --git a/app/controllers/admins/system_notifications_controller.rb b/app/controllers/admins/system_notifications_controller.rb index e2081f1a2..33f3f20f1 100644 --- a/app/controllers/admins/system_notifications_controller.rb +++ b/app/controllers/admins/system_notifications_controller.rb @@ -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 diff --git a/app/controllers/site_pages_controller.rb b/app/controllers/site_pages_controller.rb index 903e037db..085f1b027 100644 --- a/app/controllers/site_pages_controller.rb +++ b/app/controllers/site_pages_controller.rb @@ -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 diff --git a/app/forms/organizations/create_form.rb b/app/forms/organizations/create_form.rb index 2163f477e..1f40350b3 100644 --- a/app/forms/organizations/create_form.rb +++ b/app/forms/organizations/create_form.rb @@ -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 diff --git a/app/jobs/daily_platform_statistics_job.rb b/app/jobs/daily_platform_statistics_job.rb new file mode 100644 index 000000000..5610f304b --- /dev/null +++ b/app/jobs/daily_platform_statistics_job.rb @@ -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 diff --git a/app/jobs/daily_project_statistics_job.rb b/app/jobs/daily_project_statistics_job.rb new file mode 100644 index 000000000..ad606f137 --- /dev/null +++ b/app/jobs/daily_project_statistics_job.rb @@ -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 \ No newline at end of file diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 810474609..9fd858bf3 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -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) +# + diff --git a/app/models/daily_platform_statistic.rb b/app/models/daily_platform_statistic.rb new file mode 100644 index 000000000..a904d9b1e --- /dev/null +++ b/app/models/daily_platform_statistic.rb @@ -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 diff --git a/app/models/daily_project_statistic.rb b/app/models/daily_project_statistic.rb new file mode 100644 index 000000000..f7fc0aad7 --- /dev/null +++ b/app/models/daily_project_statistic.rb @@ -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 diff --git a/app/models/issue.rb b/app/models/issue.rb index edc743b0e..1c82d7120 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -36,12 +36,17 @@ # 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) diff --git a/app/models/issue_tag.rb b/app/models/issue_tag.rb index a3782abaf..f5f0abbd8 100644 --- a/app/models/issue_tag.rb +++ b/app/models/issue_tag.rb @@ -14,6 +14,7 @@ # gid :integer # gitea_url :string(255) # pull_requests_count :integer default("0") +# pm_project_id :integer # # Indexes # diff --git a/app/models/journal.rb b/app/models/journal.rb index c22dcaeec..4ee37714b 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -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) diff --git a/app/models/organization.rb b/app/models/organization.rb index cb829626e..237efbfe9 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -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, diff --git a/app/models/page.rb b/app/models/page.rb index 57a30acad..1c606760e 100644 --- a/app/models/page.rb +++ b/app/models/page.rb @@ -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 diff --git a/app/models/project.rb b/app/models/project.rb index 5f9fcef68..34e981508 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index 8f1e29547..8ffa5b181 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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]) } @@ -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 diff --git a/app/services/api/v1/issues/list_service.rb b/app/services/api/v1/issues/list_service.rb index 184eaa4f1..983e97c18 100644 --- a/app/services/api/v1/issues/list_service.rb +++ b/app/services/api/v1/issues/list_service.rb @@ -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' diff --git a/app/services/baidu/tongji_service.rb b/app/services/baidu/tongji_service.rb new file mode 100644 index 000000000..8a611cd0f --- /dev/null +++ b/app/services/baidu/tongji_service.rb @@ -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 diff --git a/app/services/page_service.rb b/app/services/page_service.rb index 687added1..5c166e82c 100644 --- a/app/services/page_service.rb +++ b/app/services/page_service.rb @@ -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 diff --git a/app/views/admins/carousels/index.html.erb b/app/views/admins/carousels/index.html.erb index 299d1dcfd..e9529c862 100644 --- a/app/views/admins/carousels/index.html.erb +++ b/app/views/admins/carousels/index.html.erb @@ -1,6 +1,6 @@ <% define_admin_breadcrumbs do - add_admin_breadcrumb('云上实验室', admins_laboratories_path) + add_admin_breadcrumb('导航栏配置', admins_laboratories_path) add_admin_breadcrumb('轮播图') end %> diff --git a/app/views/admins/dashboards/_baidu_tongji.html.erb b/app/views/admins/dashboards/_baidu_tongji.html.erb new file mode 100644 index 000000000..c59e1acad --- /dev/null +++ b/app/views/admins/dashboards/_baidu_tongji.html.erb @@ -0,0 +1,50 @@ +
+ 数据来源百度统计,本周 [<%= @current_week_statistic.first&.date %> / <%= @current_week_statistic.last&.date %>] +
+ + + + + + + + + + + + + + + <% @current_week_statistic.each_with_index do |week, index| %> + + + + + + + + + + + + <% end %> + + + + + + + + +
日期访问量访客数IP数直接访问占比外部链接占比搜索引擎占比自定义
<%= week.date %> <%= week.pv %><%= week.visitor %><%= week.ip %><%= week.source_through %>%<%= week.source_link %>%<%= week.source_search %>%<%= week.source_custom.to_f %>%
+ + +<% unless @access_token.present? && @overview_data.present? %> +
+ 1.百度统计需人工授权,点击去授权 +
+
+ 2.获取百度统计授权码 + +
+<% end %> \ No newline at end of file diff --git a/app/views/admins/dashboards/_baidu_tongji_api.html.erb b/app/views/admins/dashboards/_baidu_tongji_api.html.erb new file mode 100644 index 000000000..3a18dc405 --- /dev/null +++ b/app/views/admins/dashboards/_baidu_tongji_api.html.erb @@ -0,0 +1,67 @@ +<% if @access_token.present? && @overview_data.present? %> +
+ 数据来源百度统计,本周 <%= @overview_data['timeSpan'] %> +
+ + + + + + + + + + + <% 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] %> + + + + + + + + <% pv_count.push(pv) %> + <% visitor_count.push(visitor) %> + <% ip_count.push(ip) %> + <% end %> + + + + + + + + +
日期访问量访客数IP数
<%= date[0] %> <%= pv %><%= visitor %><%= ip %>
合计<%= pv_count %><%= visitor_count %><%= ip_count %>
+ + + + + + + + + + + + <% @source_from_data['items'][1].each_with_index do |source, index| %> + + <% end %> + + +
直接访问占比外部链接占比搜索引擎占比自定义
<%= ((source[0].to_f / @source_from_data['sum'][0][0].to_f) * 100).round(2).to_s %>%
+<% else %> +
+ 1.百度统计需人工授权,点击去授权 +
+
+ 2.获取百度统计授权码 + +
+<% end %> \ No newline at end of file diff --git a/app/views/admins/dashboards/index.html.erb b/app/views/admins/dashboards/index.html.erb index 2d86b4b6c..35593f169 100644 --- a/app/views/admins/dashboards/index.html.erb +++ b/app/views/admins/dashboards/index.html.erb @@ -1,6 +1,35 @@ <% define_admin_breadcrumbs do %> <% add_admin_breadcrumb('概览', admins_path) %> <% end %> +
+
+
+ +
+ <%@subject_name.each_with_index do |subject, index| %> +
+
+
+
+
+
<%=subject %>
+ <%= @subject_data[index] %> +
+
+
+ +
+
+
+
+
+
+ <% end %> + +
+
+
+
@@ -53,6 +82,7 @@
+ <%= render partial: 'admins/dashboards/baidu_tongji' %>
\ No newline at end of file diff --git a/app/views/admins/laboratories/index.html.erb b/app/views/admins/laboratories/index.html.erb index 8811f4ab3..adf34d6f4 100644 --- a/app/views/admins/laboratories/index.html.erb +++ b/app/views/admins/laboratories/index.html.erb @@ -1,5 +1,5 @@ <% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('云上实验室') %> + <% add_admin_breadcrumb('导航栏配置') %> <% end %>
diff --git a/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb b/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb index 0a77477d3..77ec0bd8b 100644 --- a/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb +++ b/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb @@ -2,7 +2,7 @@