diff --git a/app/api/mobile/apis/project_packages.rb b/app/api/mobile/apis/project_packages.rb index 1fa7715d..591e54b1 100644 --- a/app/api/mobile/apis/project_packages.rb +++ b/app/api/mobile/apis/project_packages.rb @@ -3,6 +3,8 @@ module Mobile module Apis class ProjectPackages < Grape::API + use Mobile::Middleware::NotFoundHandler + helpers do def current_package @_current_package ||= ProjectPackage.find(params[:id]) @@ -19,6 +21,7 @@ module Mobile optional :category, type: String, desc: '类型' optional :keyword, type: String, desc: '搜索关键字' optional :sort_by, type: String, desc: '排序' + optional :sort_direction, type: String, desc: '排序方向' optional :page, type: Integer, desc: '页数' optional :per_page, type: Integer, desc: '分页大小' end @@ -32,11 +35,10 @@ module Mobile count = packages.count - if params[:sort_by] == 'price' - packages = packages.order('min_price desc') - else - packages = packages.order('published_at desc') - end + direction = params[:sort_direction] == 'asc' ? 'asc' : 'desc' + sort = params[:sort_by] == 'price' ? 'min_price' : 'published_at' + packages = packages.order("#{sort} #{direction}") + packages = paginate packages.includes(:creator, :attachments, bidding_users: :user) present :count, count @@ -51,8 +53,13 @@ module Mobile route_param :id do get do authenticate! + user = current_user + + unless current_package.visitable? || user.id == current_package.creator_id || user.admin? + error!('403 forbidden', 403) + end - present current_package, with: Mobile::Entities::ProjectPackage, type: :show, user: current_user + present current_package, with: Mobile::Entities::ProjectPackage, type: :show, user: user end resource :bidding_users do diff --git a/app/api/mobile/entities/project_package.rb b/app/api/mobile/entities/project_package.rb index fdf94a32..de0e7218 100644 --- a/app/api/mobile/entities/project_package.rb +++ b/app/api/mobile/entities/project_package.rb @@ -32,7 +32,7 @@ module Mobile { id: package.creator.id, login: package.creator.login, - name: package.creator.show_real_name, + name: package.creator.show_name, avatar_url: "/images/#{url_to_avatar(package.creator)}" } end @@ -54,7 +54,7 @@ module Mobile { id: user.id, login: user.login, - name: user.name, + name: user.show_name, avatar_url: "/images/#{url_to_avatar(user)}", status: bidding_user.status } diff --git a/app/api/mobile/middleware/not_found_handler.rb b/app/api/mobile/middleware/not_found_handler.rb new file mode 100644 index 00000000..155b6f6a --- /dev/null +++ b/app/api/mobile/middleware/not_found_handler.rb @@ -0,0 +1,20 @@ +#coding=utf-8 + + +module Mobile + module Middleware + class NotFoundHandler < Grape::Middleware::Base + def call!(env) + @env = env + begin + @app.call(@env) + rescue ActiveRecord::RecordNotFound => e + message = {status: 404, message: e.message }.to_json + status = 200 + headers = { 'Content-Type' => content_type } + Rack::Response.new([message], status, headers).finish + end + end + end + end +end diff --git a/app/controllers/project_packages_controller.rb b/app/controllers/project_packages_controller.rb index 3945f978..fe2c1c13 100644 --- a/app/controllers/project_packages_controller.rb +++ b/app/controllers/project_packages_controller.rb @@ -25,6 +25,18 @@ class ProjectPackagesController < ApplicationController render_react end + def destroy + package = ProjectPackage.find(params[:id]) + return render_403 unless package.deletable? && (admin_or_business? || package.creator_id == current_user.id) + + package.destroy + + Tiding.create(user_id: package.creator_id, trigger_user_id: 1, container_id: package.id, + container_type: 'ProjectPackage', tiding_type: 'Destroyed', extra: package.title) + + render json: { status: 0, message: 'success' } + end + private def render_react render file: 'public/react/build/index.html', :layout => false diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3306326e..66b6fde0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -2425,6 +2425,8 @@ class UsersController < ApplicationController when "a_bank", "m_bank", "p_bank" @sort = params[:sort] || "updated_at" @p = params[:p] || "Common" + when 'a_package', 'l_package', 'p_package' + @p = params[:p] || 'a' end case @type @@ -2565,7 +2567,7 @@ class UsersController < ApplicationController if @show_all status = - case params[:q] + case params[:p] when '0' then %w(pending applying refused) when '1' then %w(published) when '2' then %w(bidding_ended bidding_finished) @@ -2578,13 +2580,14 @@ class UsersController < ApplicationController packages = packages.where(status: %w(published bidding_ended bidding_finished)) end + @per_page = 20 ids = bidding_packages.pluck(:id) + packages.pluck(:id) @objects = ProjectPackage.where(id: ids).order("published_at #{order}") when 'p_package' packages = @user.project_packages if @show_all status = - case params[:q] + case params[:p] when '0' then %w(pending applying refused) when '1' then %w(published) when '2' then %w(bidding_ended bidding_finished) @@ -2595,18 +2598,20 @@ class UsersController < ApplicationController else packages = packages.where(status: %w(published bidding_ended bidding_finished)) end + @per_page = 20 @objects = packages.order("published_at #{order}") when 'l_package' packages = @user.bidding_project_packages if @show_all status = - case params[:q] + case params[:p] when '0' then %w(bidding_lost) when '1' then %w(bidding_won) end packages = packages.where(bidding_users: { status: status }) if status.present? end + @per_page = 20 @objects = packages.order("published_at #{order}") end @objects_count = @objects.size @@ -2625,7 +2630,7 @@ class UsersController < ApplicationController @objects = @objects.to_a @objects.unshift("new") if @new_icon - @objects = paginateHelper @objects, 16 + @objects = paginateHelper @objects, @per_page || 16 respond_to do |format| format.js diff --git a/app/models/project_package.rb b/app/models/project_package.rb index b9b6ac46..08d2c612 100644 --- a/app/models/project_package.rb +++ b/app/models/project_package.rb @@ -56,10 +56,18 @@ class ProjectPackage < ActiveRecord::Base end end + def visitable? + !editable? + end + def editable? pending? || applying? || refused? end + def deletable? + pending? || refused? + end + def bidding_end? flag = deadline_at < Time.now end_bidding! if flag && can_end_bidding? @@ -73,4 +81,8 @@ class ProjectPackage < ActiveRecord::Base def increment_visit_count! ProjectPackage.connection.execute("update project_packages set visit_count = COALESCE(visit_count, 0) + 1 where id = #{id}") end + + def category_text + I18n.t("project_package.category.#{category}") + end end \ No newline at end of file diff --git a/app/services/project_packages/apply_publish_service.rb b/app/services/project_packages/apply_publish_service.rb index 45aee77b..0cf54259 100644 --- a/app/services/project_packages/apply_publish_service.rb +++ b/app/services/project_packages/apply_publish_service.rb @@ -10,7 +10,7 @@ class ProjectPackages::ApplyPublishService def call return if package.applying? - raise Error, '该状态下不能申请发布' if package.can_apply? + raise Error, '该状态下不能申请发布' unless package.may_apply? ActiveRecord::Base.transaction do package.apply! diff --git a/app/services/project_packages/end_bidding_service.rb b/app/services/project_packages/end_bidding_service.rb index 86be02c7..f7fa7b59 100644 --- a/app/services/project_packages/end_bidding_service.rb +++ b/app/services/project_packages/end_bidding_service.rb @@ -21,6 +21,6 @@ class ProjectPackages::EndBiddingService end def package_deadline? - package.deadline_at < Time.now + package.may_end_bidding? && package.deadline_at < Time.now end end \ No newline at end of file diff --git a/app/services/project_packages/save_service.rb b/app/services/project_packages/save_service.rb index 550ef346..9011722c 100644 --- a/app/services/project_packages/save_service.rb +++ b/app/services/project_packages/save_service.rb @@ -22,10 +22,9 @@ class ProjectPackages::SaveService send_create_notify! if is_create - ProjectPackages::ApplyPublishService.call(package) if with_publish? + ProjectPackages::ApplyPublishService.new(package).call if with_publish? end - package rescue ProjectPackages::ApplyPublishService::Error => ex raise Error, ex.message @@ -52,7 +51,9 @@ class ProjectPackages::SaveService package.attachments.where(id: destroy_ids).delete_all new_ids = attachment_ids - old_attachment_ids - Attachment.where(id: new_ids).update_all(container_type: package.id, container_type: 'ProjectPackage') if new_ids.present? + if new_ids.present? + Attachment.where(id: new_ids, container_id: nil).update_all(container_id: package.id, container_type: 'ProjectPackage') + end end def send_create_notify! diff --git a/app/tasks/check_project_package_deadline_task.rb b/app/tasks/check_project_package_deadline_task.rb new file mode 100644 index 00000000..4ccec257 --- /dev/null +++ b/app/tasks/check_project_package_deadline_task.rb @@ -0,0 +1,12 @@ +class CheckProjectPackageDeadlineTask + def call + ProjectPackage.where(status: :published).where('deadline_at < ?', Time.now).find_each do |package| + begin + ProjectPackages::EndBiddingService.new(package).call + rescue => ex + Rails.logger.error ex.message + Rails.logger.error ex.backtrace.join('\n') + end + end + end +end diff --git a/app/views/managements/project_package_applies/_project_package_apply_list.html.erb b/app/views/managements/project_package_applies/_project_package_apply_list.html.erb index baa5944d..6e55967f 100644 --- a/app/views/managements/project_package_applies/_project_package_apply_list.html.erb +++ b/app/views/managements/project_package_applies/_project_package_apply_list.html.erb @@ -1,6 +1,6 @@ <% if @applies.present? %> <% @applies.each do |apply| %> - <% user = apply.project_package.user %> + <% user = apply.project_package.creator %> <% project_package = apply.project_package %>
diff --git a/app/views/users/_project_package.html.erb b/app/views/users/_project_package.html.erb index d8ca475e..00e69709 100644 --- a/app/views/users/_project_package.html.erb +++ b/app/views/users/_project_package.html.erb @@ -23,13 +23,14 @@ <% if @objects_count > 0 %>
<% @objects.each do |object| %> -
+ <% can_manage = @type == 'p_package' && (admin_or_business? || current_user.id == @user.id) %> +
<%= image_tag("educoder/project_packages/#{object.category}.png") %>
+ +
+
+
    + <%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => true, :flag => true, :is_new => true %> +
+
+
+
<% else %> <%= render :partial => "welcome/no_data" %> -<% end %> \ No newline at end of file +<% end %> + + \ No newline at end of file diff --git a/config/locales/project_packages/zh.yml b/config/locales/project_packages/zh.yml new file mode 100644 index 00000000..40915dfe --- /dev/null +++ b/config/locales/project_packages/zh.yml @@ -0,0 +1,11 @@ +zh: + project_package: + category: + front: 前端开发 + backend: 后端开发 + mobile: 移动开发 + database: 数据库 + cloud_compute_and_big_data: 云计算与大数据 + devops_and_test: 运维与测试 + ai: 人工智能 + other: 其它 \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index d0108fe1..78c35a76 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,7 +57,7 @@ RedmineApp::Application.routes.draw do ## oauth相关 get :publish_success, on: :collection end - resources :project_packages, only: [:index, :show, :new, :edit] do + resources :project_packages, only: [:index, :show, :new, :edit, :destroy] do get :apply_success, on: :member end diff --git a/public/stylesheets/educoder/edu-main.css b/public/stylesheets/educoder/edu-main.css index e5eea333..1c800ba3 100644 --- a/public/stylesheets/educoder/edu-main.css +++ b/public/stylesheets/educoder/edu-main.css @@ -1017,13 +1017,27 @@ html>body #ajax-indicator { position: fixed; } white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + transition: max-width .2s; + -moz-transition: max-width .2s; /* Firefox 4 */ + -webkit-transition: max-width .2s; /* Safari 和 Chrome */ + -o-transition: max-width .2s; /* Opera */ +} +.project-package-item .item-head-tags { + display: -webkit-flex; + display: flex; + align-items: center; } .project-package-item .item-head-tags span { + margin-left: 20px; + padding: 0 10px; + height: 28px; + font-size: 14px; color: white; + border-radius: 5px; } -.project-package-item .item-head-tags span.pending { color: red; } -.project-package-item .item-head-tags span.bidding_won { color: lightgreen; } -.project-package-item .item-head-tags span.bidding_lost { color: grey; } +.project-package-item .item-head-tags span.pending { background: lightcoral; } +.project-package-item .item-head-tags span.bidding_won { background: lightgreen; } +.project-package-item .item-head-tags span.bidding_lost { background: grey; } .project-package-item .item-head-blank { flex: 1; @@ -1048,6 +1062,7 @@ html>body #ajax-indicator { position: fixed; } background:rgba(255,235,213,1); border-radius:13px; } + .project-package-item .item-other { display: -webkit-flex; display: flex; @@ -1055,8 +1070,39 @@ html>body #ajax-indicator { position: fixed; } color: #999999; } .project-package-item .item-group { - flex: 1; + flex: 2; +} +.project-package-item .item-group.item-other-publish-at { + text-align: right; } .project-package-item .item-other-blank { - flex: 2 + flex: 3 +} + +.project-package-item.with-operator .item-operator { + width: 0; + transition: width .2s; + -moz-transition: width .2s; /* Firefox 4 */ + -webkit-transition: width .2s; /* Safari 和 Chrome */ + -o-transition: width .2s; /* Opera */ +} +.project-package-item.with-operator:hover .item-operator { + margin: -20px -20px -20px 20px; + padding: 20px 0; + width: 100px; + display: flex; + justify-content: space-around; + flex-direction: column; + align-items: center; + background: #f0f0f0; +} +.project-package-item.with-operator .item-operator a { + display: none; +} +.project-package-item.with-operator:hover .item-operator a { + display: block; + font-size: 20px; +} +.project-package-item.with-operator:hover .item-head-title { + max-width: 600px; } \ No newline at end of file