diff --git a/app/controllers/users/project_packages_controller.rb b/app/controllers/users/project_packages_controller.rb new file mode 100644 index 000000000..edd6bd29b --- /dev/null +++ b/app/controllers/users/project_packages_controller.rb @@ -0,0 +1,17 @@ +class Users::ProjectPackagesController < Users::BaseController + + def index + packages = Users::ProjectPackageService.call(observed_user, query_params) + + @count = packages.count + @packages = paginate(packages.includes(:project_package_category)) + + bidding_users = BiddingUser.where(project_package_id: @packages.map(&:id), user_id: observed_user.id) + bidding_users = bidding_users.group(:project_package_id).select(:project_package_id, :status) + @bidding_status_map = bidding_users.each_with_object({}) { |u, h| h[u.project_package_id] = u.status } + end + + def query_params + params.permit(:category, :status, :sort_by, :sort_direction) + end +end \ No newline at end of file diff --git a/app/models/project_package.rb b/app/models/project_package.rb index fc541097a..219f60ca4 100644 --- a/app/models/project_package.rb +++ b/app/models/project_package.rb @@ -13,6 +13,9 @@ class ProjectPackage < ApplicationRecord has_many :attachments, as: :container, dependent: :destroy + scope :visible, -> { where(status: %i[published bidding_ended bidding_finished]) } + scope :invisible, -> { where(status: %i[pending applying refused]) } + aasm(:status) do state :pending, initiali: true state :applying diff --git a/app/models/user.rb b/app/models/user.rb index 73036c73e..03c97b9f3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -123,6 +123,11 @@ class User < ApplicationRecord has_many :user_interests, dependent: :delete_all has_many :interests, through: :user_interests, source: :repertoire + # 众包 + has_many :project_packages, foreign_key: :creator_id, dependent: :destroy + has_many :bidding_users, dependent: :destroy + has_many :bidden_project_packages, through: :bidding_users, source: :project_package + # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } @@ -572,6 +577,10 @@ class User < ApplicationRecord Attendance.find_by(user_id: id)&.next_gold || 60 # 基础50,连续签到+10 end + def admin_or_business? + admin? || business? + end + protected def validate_password_length # 管理员的初始密码是5位 diff --git a/app/services/users/project_package_service.rb b/app/services/users/project_package_service.rb new file mode 100644 index 000000000..870cdc98e --- /dev/null +++ b/app/services/users/project_package_service.rb @@ -0,0 +1,76 @@ +class Users::ProjectPackageService < ApplicationService + include CustomSortable + + sort_columns :published_at, default_by: :published_at, default_direction: :desc + + attr_reader :user, :params + + def initialize(user, params) + @user = user + @params = params + end + + def call + packages = category_scope_filter + + packages = user_policy_filter(packages) + + custom_sort(packages, :published_at, params[:sort_direction]) + end + + private + + def category_scope_filter + case params[:category] + when 'bidden' then + user.bidden_project_packages + when 'manage' then + user.project_packages + else + ids = user.bidding_users.pluck(:project_package_id) + user.project_packages.pluck(:id) + ProjectPackage.where(id: ids) + end + end + + def user_policy_filter(relations) + if self_or_admin? + status_filter(relations) + else + relations.visible + end + end + + def status_filter(relations) + return relations unless self_or_admin? + + case params[:category] + when 'bidden' then bidding_status_filter(relations) + when 'manage' then package_status_filter(relations) + else relations + end + end + + def bidding_status_filter(relations) + case params[:status] + when 'bidding_lost' then + relations.where(bidding_users: { status: :bidding_lost }) + when 'bidding_won' then + relations.where(bidding_users: { status: :bidding_won }) + else + relations + end + end + + def package_status_filter(relations) + case params[:status] + when 'unpublished' then relations.invisible + when 'bidding' then relations.where(status: :published) + when 'finished' then relations.where(status: %w[bidding_ended bidding_finished]) + else relations + end + end + + def self_or_admin? + User.current&.id == user.id || User.current&.admin_or_business? + end +end \ No newline at end of file diff --git a/app/views/users/project_packages/index.json.jbuilder b/app/views/users/project_packages/index.json.jbuilder new file mode 100644 index 000000000..a2574d558 --- /dev/null +++ b/app/views/users/project_packages/index.json.jbuilder @@ -0,0 +1,20 @@ +user = observed_user + +json.count @count +json.project_packages do + json.array! @packages.each do |package| + json.extract! package, :id, :title, :status, :min_price, :max_price, :visit_count, :bidding_users_count + + is_creator = user.id == package.creator_id + json.type is_creator ? 'manage' : 'bidden' + json.category_id package.project_package_category_id + json.category_name package.category_name + + unless is_creator + json.bidden_status @bidding_status_map[package.id] + end + + json.deadline_at package.display_deadline_at + json.published_at package.display_published_at + end +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 16140e99c..bb5bc49cc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,7 @@ Rails.application.routes.draw do resource :experience_records, only: [:show] resource :grade_records, only: [:show] resource :watch, only: [:create, :destroy] + resources :project_packages, only: [:index] end diff --git a/db/migrate/20190724013024_modify_final_score_for_games.rb b/db/migrate/20190724013024_modify_final_score_for_games.rb new file mode 100644 index 000000000..e926cccd6 --- /dev/null +++ b/db/migrate/20190724013024_modify_final_score_for_games.rb @@ -0,0 +1,15 @@ +class ModifyFinalScoreForGames < ActiveRecord::Migration[5.2] + def change + grades = Grade.where(container_type: "Answer") + grades.each do |grade| + g = Game.find_by_id(grade.container_id) + if g.present? + if g.status == 2 && g.answer_open > 0 && g.final_score <= 0 && g.end_time < grade.created_at + g.update_column(:final_score, g.challenge.score) + elsif g.final_score < 0 + g.update_column(:final_score, 0) + end + end + end + end +end diff --git a/public/react/config/webpack.config.prod.js b/public/react/config/webpack.config.prod.js index 7b9795e80..0abd707af 100644 --- a/public/react/config/webpack.config.prod.js +++ b/public/react/config/webpack.config.prod.js @@ -301,8 +301,8 @@ module.exports = { }, warnings: false, compress: { - drop_debugger: true, - drop_console: true + drop_debugger: false, + drop_console: false } } }), diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index 003ef5a9b..02d8c2be5 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -2,7 +2,7 @@ import React from "react"; import axios from 'axios'; import { requestProxy } from "./indexEduplus2RequestProxy"; -import { broadcastChannelOnmessage ,SetAppModel, isDev, queryString} from 'educoder'; +import { broadcastChannelOnmessage ,SetAppModel} from 'educoder'; import { notification } from 'antd'; import './index.css' broadcastChannelOnmessage('refreshPage', () => { @@ -18,19 +18,10 @@ function locationurl(list){ } // TODO 开发期多个身份切换 -let debugType = "" -if (isDev) { - const _search = window.location.search; - let parsed = {}; - if (_search) { - parsed = queryString.parse(_search); - } - debugType = window.location.search.indexOf('debug=t') != -1 ? 'teacher' : - window.location.search.indexOf('debug=s') != -1 ? 'student' : - window.location.search.indexOf('debug=a') != -1 ? 'admin' : parsed.debug || '' -} - -window._debugType = debugType; +const debugType ="" +// window.location.search.indexOf('debug=t') != -1 ? 'teacher' : +// window.location.search.indexOf('debug=s') != -1 ? 'student' : 'admin' +// window._debugType = debugType; export function initAxiosInterceptors(props) { // TODO 避免重复的请求 https://github.com/axios/axios#cancellation @@ -121,7 +112,7 @@ export function initAxiosInterceptors(props) { // https://github.com/axios/axios/issues/583 // message.info(response.data.message || '服务端返回status -1,请联系管理员。'); // props.showSnackbar( response.data.message || '服务器异常,请联系管理员。' ) - if (window.location.pathname.startsWith('/tasks/')) { + if (window.location.pathname.startsWith('/tasks/')) { props.showSnackbar( response.data.message || '服务器异常,请联系管理员。' ) } else { notification.open({ @@ -136,7 +127,7 @@ export function initAxiosInterceptors(props) { // description: response.data.message || '服务器异常,请联系管理员。', // }); } - + throw new axios.Cancel('Operation canceled by the user.'); } // if(response.data.status === 401){ diff --git a/public/react/src/modules/courses/coursesPublic/Addcourses.js b/public/react/src/modules/courses/coursesPublic/Addcourses.js index e8bd8b9cf..922c68650 100644 --- a/public/react/src/modules/courses/coursesPublic/Addcourses.js +++ b/public/react/src/modules/courses/coursesPublic/Addcourses.js @@ -147,6 +147,8 @@ class Addcourses extends Component{ if(Addcoursestype===true){ this.props.hideAddcoursestype(); + }else{ + window.location.href = "/"; } } diff --git a/public/react/src/modules/courses/new/CoursesNew.js b/public/react/src/modules/courses/new/CoursesNew.js index c5d0009c1..1183b59e9 100644 --- a/public/react/src/modules/courses/new/CoursesNew.js +++ b/public/react/src/modules/courses/new/CoursesNew.js @@ -88,11 +88,11 @@ class CoursesNew extends Component { } componentDidUpdate(prevProps){ - if(prevProps.current_user!=this.props.current_user){ - if(this.props.current_user.user_identity==="学生"){ - window.location.href ="/403" - } - } + // if(prevProps.current_user!=this.props.current_user){ + // if(this.props.current_user.user_identity==="学生"){ + // window.location.href ="/403" + // } + // } } onChangeTimepublishs = (date, dateString) => { if(dateString===""){ @@ -132,7 +132,7 @@ class CoursesNew extends Component { e.preventDefault(); let coursesId = this.props.match.params.coursesId; - let {is_public} = this.state + let {is_public,datatime} = this.state // console.log(is_public) if (coursesId != undefined) { @@ -173,7 +173,7 @@ class CoursesNew extends Component { // console.log('Received values of form: ', values); - let {datatime} = this.state; + let url = "/courses/" + coursesId + ".json"; axios.put(url, { course_list_name: values.course, @@ -227,7 +227,7 @@ class CoursesNew extends Component { // debugger //新建 // console.log('Received values of form: ', values); - let {datatime} = this.state; + let url = "/courses.json"; axios.post(url, { course_list_name: values.course, diff --git a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js index 5f41cdefb..61ed59d7b 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js @@ -382,7 +382,7 @@ class ShixunhomeWorkItem extends Component{ 实训详情 {this.props.isAdminOrCreator()?this.editname(discussMessage.name,discussMessage.homework_id)} className={"btn colorblue ml20 font-16"}>重命名:""} {/* 设置*/} - 设置 + 设置 :""} diff --git a/public/react/src/modules/login/LoginDialog.css b/public/react/src/modules/login/LoginDialog.css index 34a711154..7be7b12cc 100644 --- a/public/react/src/modules/login/LoginDialog.css +++ b/public/react/src/modules/login/LoginDialog.css @@ -9,7 +9,7 @@ /* z-index: 20000; */ } #DialogID .dialogBox { - width: 402px; + width: 400px; } .MuiPaper-elevation24-45{ box-shadow:none !important; @@ -24,7 +24,7 @@ top: 0px !important; } #log_reg_content{ - width:400px !important; + width:405px !important; border-radius:6px; box-shadow:4px 8px 12px rgba(76,76,76,0.3); } diff --git a/public/react/src/modules/login/Trialapplication.js b/public/react/src/modules/login/Trialapplication.js index ebb4b98c2..313d5bce3 100644 --- a/public/react/src/modules/login/Trialapplication.js +++ b/public/react/src/modules/login/Trialapplication.js @@ -406,7 +406,7 @@ class Trialapplication extends Component { top: 0px !important; } #log_reg_content{ - width:400px !important; + width:405px !important; border-radius:40px !important; box-shadow:4px 8px 12px rgba(76,76,76,0.3); } diff --git a/public/react/src/modules/login/Trialapplicationysl.js b/public/react/src/modules/login/Trialapplicationysl.js index e2afac010..dc9755fbe 100644 --- a/public/react/src/modules/login/Trialapplicationysl.js +++ b/public/react/src/modules/login/Trialapplicationysl.js @@ -424,7 +424,7 @@ class Trialapplicationysl extends Component { top: 0px !important; } #log_reg_content{ - width:400px !important; + width:405px !important; border-radius:40px !important; box-shadow:4px 8px 12px rgba(76,76,76,0.3); } diff --git a/public/react/src/modules/paths/PathDetail/PathDetailIndex.js b/public/react/src/modules/paths/PathDetail/PathDetailIndex.js index bb410c1ce..7211fde5e 100644 --- a/public/react/src/modules/paths/PathDetail/PathDetailIndex.js +++ b/public/react/src/modules/paths/PathDetail/PathDetailIndex.js @@ -139,7 +139,7 @@ class PathDetailIndex extends Component{ let pathid=this.props.match.params.pathId; let url="/paths/"+pathid+".json"; axios.get(url).then((result)=>{ - if (result.data.status == 407) { + if (result.data.status == 407 || result.data.status == 401) { return; } if(result.data.allow_visit===true){ @@ -164,9 +164,11 @@ class PathDetailIndex extends Component{ let pathid=this.props.match.params.pathId; let url="/paths/"+pathid+".json"; axios.get(url).then((result)=>{ - if (result.data.status == 407) { + // TODO 403 让后台返回status 403 比较好 + if (result.data.status == 407 || result.data.status == 401) { return; } + if(result.data.allow_visit===true){ this.setState({ detailInfoList:result.data, diff --git a/public/react/src/modules/tpm/NewHeader.js b/public/react/src/modules/tpm/NewHeader.js index da8e71a40..37fac626d 100644 --- a/public/react/src/modules/tpm/NewHeader.js +++ b/public/react/src/modules/tpm/NewHeader.js @@ -487,20 +487,22 @@ submittojoinclass=(value)=>{ } showSearchOpen=(e)=>{ + debugger this.setState({ showSearchOpentype:true }) - e.stopPropagation(e);//阻止冒泡 + } hideshowSearchOpen=(e)=>{ + debugger let {setevaluatinghides}=this.state; if(setevaluatinghides===true){ this.setState({ showSearchOpentype:false, setevaluatinghides:false }) - e.stopPropagation(e);//阻止冒泡 + } } @@ -518,6 +520,7 @@ submittojoinclass=(value)=>{ } setevaluatinghides=()=>{ + debugger this.setState( { setevaluatinghides:true @@ -599,6 +602,7 @@ submittojoinclass=(value)=>{ // rolearr:["",""], // console.log("618"); // console.log(user_phone_binded); + console.log(showSearchOpentype) return (
@@ -669,8 +673,21 @@ submittojoinclass=(value)=>{ >工程认证 - -