看完rails-practice所有实例和内容,实现在订单创建之后异步队列发送邮件,完成加入购物车和订单列表等
This commit is contained in:
parent
121a18dbd0
commit
8ed7588b41
|
|
@ -3,7 +3,6 @@
|
|||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/shop.iml" filepath="$PROJECT_DIR$/.idea/shop.iml" />
|
||||
<module fileurl="file://$USER_HOME$/下载/shop3.3/.idea/shop3.3.iml" filepath="$USER_HOME$/下载/shop3.3/.idea/shop3.3.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -31,6 +31,8 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="actioncable (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="actionmailer (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="actionpack (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="actionpack-action_caching (v1.2.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="actionpack-page_caching (v1.2.4, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="actionview (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="activejob (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="activemodel (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="builder (v3.2.4, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.3.13, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="byebug (v11.0.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="cancan (v1.6.10, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="capybara (v3.15.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="childprocess (v3.0.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="chromedriver-helper (v2.1.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -54,6 +57,7 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="coffee-script-source (v1.12.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="commonjs (v0.2.7, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.10, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="connection_pool (v2.2.5, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="crass (v1.0.6, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="database_cleaner (v2.0.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="database_cleaner-active_record (v2.0.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -63,12 +67,12 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="erubi (v1.10.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="erubis (v2.7.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="execjs (v2.8.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="factory_girl (v4.9.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="factory_girl_rails (v4.9.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="factory_bot (v5.2.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.5, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="globalid (v0.4.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="haml (v5.2.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="haml-rails (v2.0.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="hiredis (v0.6.3, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="html2haml (v2.2.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="i18n (v1.10.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="io-like (v0.3.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -92,6 +96,7 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="mini_portile2 (v2.4.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.15.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="msgpack (v1.3.3, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="mustermann (v1.1.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="mysql2 (v0.5.4, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="nio4r (v2.5.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.10.10, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -99,6 +104,7 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="public_suffix (v4.0.7, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="puma (v3.12.6, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rack (v2.2.3, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rack-protection (v2.2.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rack-test (v1.1.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rails (v5.2.8, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.0.3, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -108,6 +114,13 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="ransack (v2.4.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rb-fsevent (v0.11.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rb-inotify (v0.10.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis (v4.1.4, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis-actionpack (v5.3.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis-activesupport (v5.3.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis-namespace (v1.6.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis-rack (v2.1.4, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis-rails (v5.0.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="redis-store (v1.9.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="ref (v2.0.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="regexp_parser (v1.8.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="responders (v2.4.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -116,6 +129,7 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.11.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rspec-rails (v5.1.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.11.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="ruby2_keywords (v0.0.5, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="ruby_dep (v1.5.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="ruby_parser (v3.19.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v1.3.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -124,6 +138,8 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="sass-rails (v5.0.7, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="selenium-webdriver (v3.142.7, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="sexp_processor (v4.16.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="sidekiq (v5.2.9, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="sinatra (v2.2.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="spring (v2.0.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="spring-watcher-listen (v2.0.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="sprockets (v3.7.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
|
|
@ -144,6 +160,8 @@
|
|||
<orderEntry type="library" scope="PROVIDED" name="web-console (v3.7.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="websocket-driver (v0.7.5, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="websocket-extensions (v0.1.5, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="will_paginate (v3.3.1, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="will_paginate-bootstrap (v1.0.2, rbenv: 2.3.7) [gem]" level="application" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="xpath (v3.2.0, rbenv: 2.3.7) [gem]" level="application" />
|
||||
</component>
|
||||
<component name="RModuleSettingsStorage">
|
||||
|
|
|
|||
24
Gemfile
24
Gemfile
|
|
@ -43,8 +43,30 @@ gem "haml"
|
|||
gem "html2haml"
|
||||
gem "haml-rails"
|
||||
gem 'liquid-rails'
|
||||
# 请求
|
||||
gem "responders"
|
||||
|
||||
#权限控制
|
||||
gem "cancan"
|
||||
|
||||
#分页
|
||||
gem 'will_paginate-bootstrap'
|
||||
|
||||
# Cache
|
||||
gem 'actionpack-page_caching'
|
||||
gem 'actionpack-action_caching'
|
||||
|
||||
|
||||
#Redis
|
||||
gem 'redis-rails'
|
||||
gem 'redis-namespace'
|
||||
gem "hiredis"
|
||||
|
||||
#队列
|
||||
gem 'sidekiq', '~> 5.2.9'
|
||||
gem 'sinatra', :require => nil
|
||||
# Use Redis adapter to run Action Cable in production
|
||||
# gem 'redis', '~> 4.0'
|
||||
gem 'redis', '~> 4.1.4'
|
||||
# Use ActiveModel has_secure_password
|
||||
# gem 'bcrypt', '~> 3.1.7'
|
||||
|
||||
|
|
|
|||
55
Gemfile.lock
55
Gemfile.lock
|
|
@ -18,6 +18,10 @@ GEM
|
|||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionpack-action_caching (1.2.2)
|
||||
actionpack (>= 4.0.0)
|
||||
actionpack-page_caching (1.2.4)
|
||||
actionpack (>= 4.0.0)
|
||||
actionview (5.2.8)
|
||||
activesupport (= 5.2.8)
|
||||
builder (~> 3.1)
|
||||
|
|
@ -53,6 +57,7 @@ GEM
|
|||
msgpack (~> 1.2)
|
||||
builder (3.2.4)
|
||||
byebug (11.0.1)
|
||||
cancan (1.6.10)
|
||||
capybara (3.15.1)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
|
|
@ -74,6 +79,7 @@ GEM
|
|||
coffee-script-source (1.12.2)
|
||||
commonjs (0.2.7)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.2.5)
|
||||
crass (1.0.6)
|
||||
database_cleaner (2.0.1)
|
||||
database_cleaner-active_record (~> 2.0.0)
|
||||
|
|
@ -105,6 +111,7 @@ GEM
|
|||
haml (>= 4.0.6, < 6.0)
|
||||
html2haml (>= 1.0.1)
|
||||
railties (>= 5.1)
|
||||
hiredis (0.6.3)
|
||||
html2haml (2.2.0)
|
||||
erubis (~> 2.7.0)
|
||||
haml (>= 4.0, < 6)
|
||||
|
|
@ -159,6 +166,8 @@ GEM
|
|||
mini_portile2 (2.4.0)
|
||||
minitest (5.15.0)
|
||||
msgpack (1.3.3)
|
||||
mustermann (1.1.1)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
mysql2 (0.5.4)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.10)
|
||||
|
|
@ -167,6 +176,8 @@ GEM
|
|||
public_suffix (4.0.7)
|
||||
puma (3.12.6)
|
||||
rack (2.2.3)
|
||||
rack-protection (2.2.0)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.8)
|
||||
|
|
@ -201,6 +212,25 @@ GEM
|
|||
rb-fsevent (0.11.1)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
redis (4.1.4)
|
||||
redis-actionpack (5.3.0)
|
||||
actionpack (>= 5, < 8)
|
||||
redis-rack (>= 2.1.0, < 3)
|
||||
redis-store (>= 1.1.0, < 2)
|
||||
redis-activesupport (5.3.0)
|
||||
activesupport (>= 3, < 8)
|
||||
redis-store (>= 1.3, < 2)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
redis-rack (2.1.4)
|
||||
rack (>= 2.0.8, < 3)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-rails (5.0.2)
|
||||
redis-actionpack (>= 5.0, < 6)
|
||||
redis-activesupport (>= 5.0, < 6)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.9.1)
|
||||
redis (>= 4, < 5)
|
||||
ref (2.0.0)
|
||||
regexp_parser (1.8.2)
|
||||
responders (2.4.1)
|
||||
|
|
@ -223,6 +253,7 @@ GEM
|
|||
rspec-mocks (~> 3.10)
|
||||
rspec-support (~> 3.10)
|
||||
rspec-support (3.11.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
ruby_dep (1.5.0)
|
||||
ruby_parser (3.19.1)
|
||||
sexp_processor (~> 4.16)
|
||||
|
|
@ -242,6 +273,16 @@ GEM
|
|||
childprocess (>= 0.5, < 4.0)
|
||||
rubyzip (>= 1.2.2)
|
||||
sexp_processor (4.16.1)
|
||||
sidekiq (5.2.9)
|
||||
connection_pool (~> 2.2, >= 2.2.2)
|
||||
rack (~> 2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (>= 3.3.5, < 4.2)
|
||||
sinatra (2.2.0)
|
||||
mustermann (~> 1.0)
|
||||
rack (~> 2.2)
|
||||
rack-protection (= 2.2.0)
|
||||
tilt (~> 2.0)
|
||||
spring (2.0.2)
|
||||
activesupport (>= 4.2)
|
||||
spring-watcher-listen (2.0.1)
|
||||
|
|
@ -289,6 +330,9 @@ GEM
|
|||
websocket-driver (0.7.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
will_paginate (3.3.1)
|
||||
will_paginate-bootstrap (1.0.2)
|
||||
will_paginate (>= 3.0.3)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
|
||||
|
|
@ -296,8 +340,11 @@ PLATFORMS
|
|||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
actionpack-action_caching
|
||||
actionpack-page_caching
|
||||
bootsnap (>= 1.1.0)
|
||||
byebug
|
||||
cancan
|
||||
capybara (>= 2.15)
|
||||
chromedriver-helper
|
||||
coffee-rails (~> 4.2)
|
||||
|
|
@ -306,6 +353,7 @@ DEPENDENCIES
|
|||
factory_bot
|
||||
haml
|
||||
haml-rails
|
||||
hiredis
|
||||
html2haml
|
||||
jbuilder (~> 2.5)
|
||||
jquery-rails
|
||||
|
|
@ -316,9 +364,15 @@ DEPENDENCIES
|
|||
puma (~> 3.11)
|
||||
rails (~> 5.2.8)
|
||||
ransack
|
||||
redis (~> 4.1.4)
|
||||
redis-namespace
|
||||
redis-rails
|
||||
responders
|
||||
rspec-rails (~> 5.1.2)
|
||||
sass-rails (~> 5.0)
|
||||
selenium-webdriver
|
||||
sidekiq (~> 5.2.9)
|
||||
sinatra
|
||||
spring
|
||||
spring-watcher-listen (~> 2.0.0)
|
||||
therubyracer
|
||||
|
|
@ -329,6 +383,7 @@ DEPENDENCIES
|
|||
tzinfo-data
|
||||
uglifier (>= 1.3.0)
|
||||
web-console (>= 3.3.0)
|
||||
will_paginate-bootstrap
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.3.7p456
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ jQuery ->
|
|||
$('#editProductDescription').val(data['description'])
|
||||
$('#editProductPrice').val(data['price'])
|
||||
$("#editProductForm").attr('action', '/products/'+data['id'])
|
||||
.on "ajax:error", (e, xhr, status, error) ->
|
||||
alert(JSON.parse(xhr.responseText).error)
|
||||
|
||||
|
||||
$("#editProductForm")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
require "application_responder"
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
self.responder = ApplicationResponder
|
||||
respond_to :html, :xml, :json, :js
|
||||
before_action :authenticate_user!
|
||||
skip_before_action :authenticate_user!, only: [:index, :show, :top]
|
||||
before_action :configure_permitted_parameters, if: :devise_controller?
|
||||
protected
|
||||
def configure_permitted_parameters
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
class CheckoutController < ApplicationController
|
||||
def cart
|
||||
@cart_items = current_user.cart_items
|
||||
end
|
||||
|
||||
# Cart => Checkout
|
||||
def checkout
|
||||
# OPTIMIZE
|
||||
items_params.each do |item|
|
||||
current_user.cart_items.update(item[0], quantity: item[1]["quantity"])
|
||||
end
|
||||
redirect_to address_path, notice: "确认你的收货地址"
|
||||
end
|
||||
|
||||
# GET
|
||||
# Checkout => Address
|
||||
def address
|
||||
@address = current_user.address
|
||||
if !@address
|
||||
@address=Address.new
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH
|
||||
# Address => Confirm
|
||||
def confirm
|
||||
current_user.address.update(address_params)
|
||||
@order = Order.create(user: current_user, number: Time.zone.now.strftime("%y%m%d%H%M%S"), payment_state: "confirm")
|
||||
current_user.cart_items.each do |item|
|
||||
@order.line_items << LineItem.new(quantity: item.quantity, variant: item.variant)
|
||||
end
|
||||
current_user.cart_items.destroy
|
||||
redirect_to order_path(@order.number), notice: "订单已确认,请支付。"
|
||||
end
|
||||
|
||||
# 点击支付按钮,跳转到支付宝页面
|
||||
def pay
|
||||
order = Order.find_by(number: params[:number])
|
||||
alipay_url = Alipay::Service.create_partner_trade_by_buyer_url(
|
||||
out_trade_no: order.number,
|
||||
subject: order.number,
|
||||
price: '10.00',
|
||||
quantity: 12,
|
||||
logistics_type: 'DIRECT',
|
||||
logistics_fee: '0',
|
||||
logistics_payment: 'SELLER_PAY',
|
||||
return_url: order_url(order.number),
|
||||
notify_url: notify_url
|
||||
)
|
||||
redirect_to alipay_url, notice: "应该跳转到支付页面"
|
||||
end
|
||||
|
||||
# 实时到账交易成功,返回 TRADE_SUCCESS
|
||||
def notify
|
||||
notify_params = params.except(*request.path_parameters.keys)
|
||||
logger.info notify_params
|
||||
if Alipay::Notify.verify?(notify_params) and ['TRADE_FINISHED', 'TRADE_SUCCESS'].include?(notify_params[:trade_status])
|
||||
out_trade_no = notify_params[:out_trade_no]
|
||||
order = Order.find_by(number: out_trade_no) || raise(ActiveRecord::RecordNotFound)
|
||||
if order.payment_state == "confirm"
|
||||
order.update(payment_state: "paid")
|
||||
end
|
||||
render text: "success"
|
||||
else
|
||||
render text: "fail"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def address_params
|
||||
params.require(:address).permit(:state, :city, :address, :address2, :zipcode, :receiver, :phone)
|
||||
end
|
||||
|
||||
def items_params
|
||||
params[:item].permit!
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
class OrdersController < ApplicationController
|
||||
before_action :set_order, only: [:show, :edit, :update, :destroy]
|
||||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
@orders = Order.paginate(:page => params[:page], :per_page => 3)
|
||||
respond_with(@orders)
|
||||
end
|
||||
|
||||
def show
|
||||
respond_with(@order)
|
||||
end
|
||||
|
||||
def new
|
||||
@order = Order.new
|
||||
respond_with(@order)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def create
|
||||
@order = Order.new(order_params)
|
||||
@order.save
|
||||
respond_with(@order)
|
||||
end
|
||||
|
||||
def update
|
||||
@order.update(order_params)
|
||||
respond_with(@order)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@order.destroy
|
||||
respond_with(@order)
|
||||
end
|
||||
|
||||
private
|
||||
def set_order
|
||||
@order = Order.find_by(number: params[:id])
|
||||
end
|
||||
|
||||
def order_params
|
||||
params[:order]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
class ProductsController < ApplicationController
|
||||
before_action :set_product, only: %i[ show edit update destroy ]
|
||||
caches_action :index, layout:false
|
||||
|
||||
# GET /products or /products.json
|
||||
def index
|
||||
|
|
@ -20,6 +21,11 @@ class ProductsController < ApplicationController
|
|||
def top
|
||||
end
|
||||
def buy
|
||||
@product = Product.find(params[:product_id])
|
||||
@variant = @product.variants.find(params[:variant_id])
|
||||
cart_item = CartItem.find_or_create_by(variant: @variant, user: current_user)
|
||||
cart_item.increment!(:quantity, params[:quantity].to_i)
|
||||
redirect_to @product, notice: "已添加到购物车"
|
||||
end
|
||||
# GET /products/new
|
||||
def new
|
||||
|
|
@ -38,7 +44,6 @@ class ProductsController < ApplicationController
|
|||
# POST /products or /products.json
|
||||
def create
|
||||
@product = Product.new(product_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @product.save
|
||||
format.html { redirect_to product_url(@product), notice: "Product was successfully created." }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
class OrderCreateJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(order)
|
||||
OrderMailer.confirm_email(order).deliver_now
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class OrderMailer < ApplicationMailer
|
||||
def confirm_email(order)
|
||||
@user = order.user
|
||||
@order = order
|
||||
mail(to: @user.email, subject: "您的订单 #{@order.number} 已经确认")
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
def initialize(user)
|
||||
# Define abilities for the passed in user here. For example:
|
||||
#
|
||||
# user ||= User.new # guest user (not logged in)
|
||||
# if user.admin?
|
||||
# can :manage, :all
|
||||
# else
|
||||
# can :read, :all
|
||||
# end
|
||||
#
|
||||
# The first argument to `can` is the action you are giving the user
|
||||
# permission to do.
|
||||
# If you pass :manage it will apply to every action. Other common actions
|
||||
# here are :read, :create, :update and :destroy.
|
||||
#
|
||||
# The second argument is the resource the user can perform the action on.
|
||||
# If you pass :all it will apply to every resource. Otherwise pass a Ruby
|
||||
# class of the resource.
|
||||
#
|
||||
# The third argument is an optional hash of conditions to further filter the
|
||||
# objects.
|
||||
# For example, here the user can only update published articles.
|
||||
#
|
||||
# can :update, Article, :published => true
|
||||
#
|
||||
# See the wiki for details:
|
||||
# https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
||||
#
|
||||
user ||= User.new # guest user (not logged in)
|
||||
if user.role=='admin'
|
||||
can :manage, :all
|
||||
else
|
||||
can :read, :all
|
||||
can :manage, Address, :user_id => user.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
class Address < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :user,inverse_of: :address
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
class CartItem < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :variant
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class Catalog < ApplicationRecord
|
||||
has_many :subcatalogs, class_name: "Catalog", foreign_key: "parent_catalog_id"
|
||||
belongs_to :parent_catalog, class_name: "Catalog"
|
||||
has_many :products
|
||||
end
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
class LineItem < ApplicationRecord
|
||||
belongs_to :order
|
||||
belongs_to :variant
|
||||
belongs_to :user
|
||||
delegate :price, to: :variant, prefix: true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,20 @@
|
|||
class Order < ApplicationRecord
|
||||
class Order < ActiveRecord::Base
|
||||
belongs_to :user,counter_cache: true
|
||||
has_many :line_items
|
||||
has_many :line_items, -> { includes :variant }
|
||||
has_many :variants, through: :line_items
|
||||
validates :card_number, presence: true, if: :paid_with_card?
|
||||
|
||||
after_create :send_create_email
|
||||
def send_create_email
|
||||
OrderCreateJob.perform_later(self)
|
||||
end
|
||||
|
||||
def paid_with_card?
|
||||
payment_state == "card"
|
||||
end
|
||||
after_create do
|
||||
line_items.each do |line_item|
|
||||
line_item.variant.decrement!(:on_hand, line_item.quantity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
class Person
|
||||
extend ActiveModel::Callbacks
|
||||
define_model_callbacks :create
|
||||
|
||||
def create
|
||||
run_callbacks :create do
|
||||
puts "I am in create method."
|
||||
end
|
||||
end
|
||||
|
||||
before_create :action_before_create
|
||||
def action_before_create
|
||||
puts "I am in before action of create."
|
||||
end
|
||||
|
||||
after_create :action_after_create
|
||||
def action_after_create
|
||||
puts "I am in after action of create."
|
||||
end
|
||||
|
||||
around_create :action_around_create
|
||||
def action_around_create
|
||||
puts "I am in around action of create."
|
||||
yield
|
||||
puts "I am in around action of create."
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -3,4 +3,41 @@ class Product < ActiveRecord::Base
|
|||
scope :state, -> { where(state:1) }
|
||||
scope :is_delete, -> { where("deleted_at is not null") }
|
||||
has_many :variants
|
||||
accepts_nested_attributes_for :variants
|
||||
before_validation do
|
||||
puts "before_validation"
|
||||
end
|
||||
after_validation do
|
||||
puts "after_validation"
|
||||
end
|
||||
before_save do
|
||||
puts "before_save"
|
||||
end
|
||||
around_save :test_around_save
|
||||
def test_around_save
|
||||
puts "begin around_save"
|
||||
yield
|
||||
puts "end around_save"
|
||||
end
|
||||
before_create do
|
||||
puts "before_create"
|
||||
end
|
||||
around_create :test_around_create
|
||||
def test_around_create
|
||||
puts "begin around_create"
|
||||
yield
|
||||
puts "end around_create"
|
||||
end
|
||||
after_create do
|
||||
puts "after_create"
|
||||
end
|
||||
after_save do
|
||||
puts "after_save"
|
||||
end
|
||||
after_commit do
|
||||
puts "after_commit"
|
||||
end
|
||||
after_rollback do
|
||||
puts "after_rollback"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ class User < ApplicationRecord
|
|||
devise :database_authenticatable, :registerable,
|
||||
:recoverable, :rememberable, :validatable,
|
||||
:confirmable
|
||||
has_one :address,dependent: :destroy
|
||||
has_one :address,dependent: :destroy,inverse_of: :user
|
||||
accepts_nested_attributes_for :address, update_only: true
|
||||
has_many :orders
|
||||
has_and_belongs_to_many :variants
|
||||
has_many :cart_items, -> { includes(:variant).order("cart_items.updated_at asc") }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,2 +1,6 @@
|
|||
class Variant < ApplicationRecord
|
||||
belongs_to :product
|
||||
has_many :line_items
|
||||
has_many :orders, through: :line_items
|
||||
has_and_belongs_to_many :users
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
<%- model_class = Product -%>
|
||||
<div class="page-header">
|
||||
<h1>收货地址</h1>
|
||||
</div>
|
||||
|
||||
<%= form_for @address, url: confirm_path do |f| %>
|
||||
<div class="row">
|
||||
<div class="col col-md-8">
|
||||
<div class="form-group">
|
||||
<%= f.label :state, class: "control-label" %><br />
|
||||
<%= f.text_field :state, class: "form-control" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :city, class: "control-label" %><br />
|
||||
<%= f.text_field :city, class: "form-control" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :address, class: "control-label" %><br />
|
||||
<%= f.text_field :address, class: "form-control" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :address2, class: "control-label" %><br />
|
||||
<%= f.text_field :address2, class: "form-control" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :zipcode, class: "control-label" %><br />
|
||||
<%= f.text_field :zipcode, class: "form-control" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :receiver, class: "control-label" %><br />
|
||||
<%= f.text_field :receiver, class: "form-control" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :phone, class: "control-label" %><br />
|
||||
<%= f.text_field :phone, class: "form-control" %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-footer">
|
||||
<%= submit_tag "确认订单", :class => 'btn btn-info btn-block' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<%- model_class = Product -%>
|
||||
<div class="page-header">
|
||||
<h1>购物车</h1>
|
||||
</div>
|
||||
|
||||
<% if @cart_items.empty? %>
|
||||
购物车为空
|
||||
<% else %>
|
||||
<%= form_tag checkout_path do %>
|
||||
<div class="row">
|
||||
<div class="col col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">商品</th>
|
||||
<th width="10%">大小</th>
|
||||
<th class="text-right" width="10%">价格</th>
|
||||
<th width="10%">数量</th>
|
||||
<th width="20%" class="text-right">合计</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<% @cart_items.each do |item| %>
|
||||
<tr>
|
||||
<td><%= item.variant.product.name %></td>
|
||||
<td><%= item.variant.size %></td>
|
||||
<td class="text-right"><%= item.variant.price %></td>
|
||||
<td><%= text_field_tag "item[#{item.id}][quantity]", item.quantity %></td>
|
||||
<td class="text-right"><%= number_to_currency item.quantity * item.variant.price %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-footer">
|
||||
<%= submit_tag "确认收货地址", :class => 'btn btn-info btn-block' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
@ -50,8 +50,7 @@
|
|||
<div class="navbar-collapse collapse navbar-responsive-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><%= link_to "网店首页", "/" %></li>
|
||||
<li><%= link_to "我的订单", "/path2" %></li>
|
||||
<li><%= link_to "分类", "/path3" %></li>
|
||||
<li><%= link_to "我的订单", "/orders" %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<h1>你好, <%= @user.email %></h1>
|
||||
<p>
|
||||
您的订单 <%= @order.number %> 已经确认。
|
||||
</p>
|
||||
|
|
@ -0,0 +1 @@
|
|||
你好, <%= @user.email %>,您的订单 <%= @order.number %> 已经确认。
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<%= form_for(@order) do |f| %>
|
||||
<% if @order.errors.any? %>
|
||||
<div id="error_explanation">
|
||||
<h2><%= pluralize(@order.errors.count, "error") %> prohibited this order from being saved:</h2>
|
||||
|
||||
<ul>
|
||||
<% @order.errors.full_messages.each do |message| %>
|
||||
<li><%= message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<h1>Editing Order</h1>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Show', @order %> |
|
||||
<%= link_to 'Back', orders_path %>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<div class="page-header">
|
||||
<h1>我的订单</h1>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">订单</th>
|
||||
<th width="10%">总额</th>
|
||||
<th width="10%">状态</th>
|
||||
<th width="20%">创建时间</th>
|
||||
<th width="10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<% @orders.each do |order| %>
|
||||
<tr>
|
||||
<td><%= link_to order.number, order %></td>
|
||||
<td><%= number_to_currency order.line_items.map{|i| i.quantity * i.variant_price}.sum %></td>
|
||||
<td><%= order.payment_state %></td>
|
||||
<td><%= order.created_at.to_date %></td>
|
||||
<td><%= link_to "支付", pay_path(number: order.number), method: :post if order.payment_state == "confirm" %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="well">
|
||||
<%= page_entries_info @orders %>
|
||||
</div>
|
||||
<%= will_paginate @orders, renderer: BootstrapPagination::Rails %>
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
json.array!(@orders) do |order|
|
||||
json.extract! order, :id
|
||||
json.url order_url(order, format: :json)
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<h1>New Order</h1>
|
||||
|
||||
<%= render 'form' %>
|
||||
|
||||
<%= link_to 'Back', orders_path %>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<div class="page-header">
|
||||
<h1>订单:<%= @order.number %></h1>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">商品</th>
|
||||
<th width="10%">大小</th>
|
||||
<th class="text-right" width="10%">价格</th>
|
||||
<th class="text-center" width="10%">数量</th>
|
||||
<th width="20%" class="text-right">合计</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<% @order.line_items.each do |item| %>
|
||||
<tr>
|
||||
<td><%= link_to item.variant.product_name, product_path(item.variant.product_id) %></td>
|
||||
<td><%= item.variant.size %></td>
|
||||
<td class="text-right"><%= item.variant_price %></td>
|
||||
<td class="text-center"><%= item.quantity %></td>
|
||||
<td class="text-right"><%= number_to_currency item.quantity * item.variant_price %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="text-right" colspan="5"><%= number_to_currency @order.line_items.map{|item| item.quantity * item.variant_price}.sum %></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<%= link_to "使用 支付宝 支付该订单", pay_path(number: @order.number), method: :post, data: { confirm: "使用支付宝支付" }, class: "btn btn-info btn-block" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
json.extract! @order, :id, :created_at, :updated_at
|
||||
|
|
@ -11,7 +11,42 @@
|
|||
<dt><strong><%= model_class.human_attribute_name(:description) %>:</strong></dt>
|
||||
<dd><%= @product.description %></dd>
|
||||
</dl>
|
||||
|
||||
<%= form_tag buy_product_path(@product) do %>
|
||||
<div class="row">
|
||||
<div class="col col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading product-section-title">选择您想购买的商品</div>
|
||||
<ul class="list-group">
|
||||
<% @product.variants.each_with_index do |variant, index| %>
|
||||
<li class="list-group-item">
|
||||
<label>
|
||||
<%= hidden_field_tag "product_id", @product.id %>
|
||||
<%= radio_button_tag "variant_id", variant.id, index == 0 %>
|
||||
<%= variant.size %>
|
||||
<%= number_to_currency variant.price %>
|
||||
</label>
|
||||
</li>
|
||||
<% end%>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body input-group">
|
||||
<%= number_field_tag :quantity, 1, :class => 'form-control', :min => 1, style: "text-align: center;" %>
|
||||
<span class="input-group-btn">
|
||||
<%= button_tag :class => 'btn btn-success', :type => :submit do %>
|
||||
添加到购物车
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<%= link_to "查看购物车", cart_path, :class => 'btn btn-info btn-block' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= link_to t('.back', :default => t("helpers.links.back")),
|
||||
products_path, :class => 'btn btn-default' %>
|
||||
<%= link_to t('.edit', :default => t("helpers.links.edit")),
|
||||
|
|
|
|||
|
|
@ -8,12 +8,17 @@ Bundler.require(*Rails.groups)
|
|||
|
||||
module Shop
|
||||
class Application < Rails::Application
|
||||
# Use the responders controller from the responders gem
|
||||
config.app_generators.scaffold_controller :responders_controller
|
||||
|
||||
# Initialize configuration defaults for originally generated Rails version.
|
||||
config.load_defaults 5.2
|
||||
|
||||
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**/*.{rb,yml}').to_s]
|
||||
config.i18n.default_locale = :"zh-CN"
|
||||
# Settings in config/environments/* take precedence over those specified here.
|
||||
# Application configuration can go into files in config/initializers
|
||||
# -- all .rb files in that directory are automatically loaded after loading
|
||||
# the framework and any gems in your application.
|
||||
config.active_job.queue_adapter = :sidekiq
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ Rails.application.configure do
|
|||
config.active_storage.service = :local
|
||||
|
||||
# Don't care if the mailer can't send.
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
|
|
@ -60,14 +60,14 @@ Rails.application.configure do
|
|||
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||
# 邮箱配置
|
||||
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
||||
config.action_mailer.perform_deliveries = true
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
config.action_mailer.smtp_settings = {
|
||||
address: 'smtp.163.com',
|
||||
port: 25,
|
||||
user_name: 'a410928612@163.com',
|
||||
password: '834752287ZSXzsx',
|
||||
authentication: :plain,
|
||||
address: 'smtp.163.com',
|
||||
port: 25,
|
||||
user_name: 'a410928612@163.com',
|
||||
password: 'RHSUARDKLUPZJVJA',
|
||||
authentication: :plain,
|
||||
enable_starttls_auto: true
|
||||
}
|
||||
config.action_controller.page_cache_directory = "#{Rails.root.to_s}/public"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
require "sidekiq"
|
||||
|
||||
redis_hash = {
|
||||
namespace: 'sidekiq',
|
||||
host: "127.0.0.1",
|
||||
port: 6379
|
||||
}
|
||||
Sidekiq.configure_server do |config|
|
||||
config.redis = redis_hash
|
||||
end
|
||||
Sidekiq.configure_client do |config|
|
||||
config.redis = redis_hash
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Sample localization file for English. Add more files in this directory for other locales.
|
||||
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
|
||||
|
||||
zh-CN:
|
||||
breadcrumbs:
|
||||
application:
|
||||
root: "首页"
|
||||
pages:
|
||||
pages: "Pages"
|
||||
helpers:
|
||||
actions: "动作"
|
||||
links:
|
||||
back: "返回"
|
||||
cancel: "取消"
|
||||
confirm: "确认"
|
||||
destroy: "删除"
|
||||
new: "添加"
|
||||
edit: "编辑"
|
||||
sign_in: 登录
|
||||
sign_up: 注册
|
||||
update: 更新
|
||||
forgot_password: 找回密码
|
||||
send_password: 发送找回邮件
|
||||
change_password: 修改密码
|
||||
titles:
|
||||
edit: "编辑 %{model}"
|
||||
save: "保存 %{model}"
|
||||
new: "新建 %{model}"
|
||||
delete: "删除 %{model}"
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
# https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/zh-CN.yml
|
||||
---
|
||||
zh-CN:
|
||||
date:
|
||||
abbr_day_names:
|
||||
- 周日
|
||||
- 周一
|
||||
- 周二
|
||||
- 周三
|
||||
- 周四
|
||||
- 周五
|
||||
- 周六
|
||||
abbr_month_names:
|
||||
-
|
||||
- 1月
|
||||
- 2月
|
||||
- 3月
|
||||
- 4月
|
||||
- 5月
|
||||
- 6月
|
||||
- 7月
|
||||
- 8月
|
||||
- 9月
|
||||
- 10月
|
||||
- 11月
|
||||
- 12月
|
||||
day_names:
|
||||
- 星期日
|
||||
- 星期一
|
||||
- 星期二
|
||||
- 星期三
|
||||
- 星期四
|
||||
- 星期五
|
||||
- 星期六
|
||||
formats:
|
||||
default: "%Y-%m-%d"
|
||||
long: "%Y年%b%d日"
|
||||
short: "%b%d日"
|
||||
month_names:
|
||||
-
|
||||
- 一月
|
||||
- 二月
|
||||
- 三月
|
||||
- 四月
|
||||
- 五月
|
||||
- 六月
|
||||
- 七月
|
||||
- 八月
|
||||
- 九月
|
||||
- 十月
|
||||
- 十一月
|
||||
- 十二月
|
||||
order:
|
||||
- :year
|
||||
- :month
|
||||
- :day
|
||||
datetime:
|
||||
distance_in_words:
|
||||
about_x_hours:
|
||||
one: 大约一小时
|
||||
other: 大约 %{count} 小时
|
||||
about_x_months:
|
||||
one: 大约一个月
|
||||
other: 大约 %{count} 个月
|
||||
about_x_years:
|
||||
one: 大约一年
|
||||
other: 大约 %{count} 年
|
||||
almost_x_years:
|
||||
one: 接近一年
|
||||
other: 接近 %{count} 年
|
||||
half_a_minute: 半分钟
|
||||
less_than_x_minutes:
|
||||
one: 不到一分钟
|
||||
other: 不到 %{count} 分钟
|
||||
less_than_x_seconds:
|
||||
one: 不到一秒
|
||||
other: 不到 %{count} 秒
|
||||
over_x_years:
|
||||
one: 一年多
|
||||
other: "%{count} 年多"
|
||||
x_days:
|
||||
one: 一天
|
||||
other: "%{count} 天"
|
||||
x_minutes:
|
||||
one: 一分钟
|
||||
other: "%{count} 分钟"
|
||||
x_months:
|
||||
one: 一个月
|
||||
other: "%{count} 个月"
|
||||
x_seconds:
|
||||
one: 一秒
|
||||
other: "%{count} 秒"
|
||||
prompts:
|
||||
day: 日
|
||||
hour: 时
|
||||
minute: 分
|
||||
month: 月
|
||||
second: 秒
|
||||
year: 年
|
||||
errors:
|
||||
format: "%{attribute}%{message}"
|
||||
messages:
|
||||
accepted: 必须是可被接受的
|
||||
blank: 不能为空字符
|
||||
present: 必须是空白
|
||||
confirmation: 与确认值不匹配
|
||||
empty: 不能留空
|
||||
equal_to: 必须等于 %{count}
|
||||
even: 必须为双数
|
||||
exclusion: 是保留关键字
|
||||
greater_than: 必须大于 %{count}
|
||||
greater_than_or_equal_to: 必须大于或等于 %{count}
|
||||
inclusion: 不包含于列表中
|
||||
invalid: 是无效的
|
||||
less_than: 必须小于 %{count}
|
||||
less_than_or_equal_to: 必须小于或等于 %{count}
|
||||
not_a_number: 不是数字
|
||||
not_an_integer: 必须是整数
|
||||
odd: 必须为单数
|
||||
record_invalid: '验证失败: %{errors}'
|
||||
restrict_dependent_destroy:
|
||||
one: 由于 %{record} 需要此记录,所以无法移除记录
|
||||
many: 由于 %{record} 需要此记录,所以无法移除记录
|
||||
taken: 已经被使用
|
||||
too_long:
|
||||
one: 过长(最长为一个字符)
|
||||
other: 过长(最长为 %{count} 个字符)
|
||||
too_short:
|
||||
one: 过短(最短为一个字符)
|
||||
other: 过短(最短为 %{count} 个字符)
|
||||
wrong_length:
|
||||
one: 长度非法(必须为一个字符)
|
||||
other: 长度非法(必须为 %{count} 个字符)
|
||||
other_than: 长度非法(不可为 %{count} 个字符
|
||||
template:
|
||||
body: 如下字段出现错误:
|
||||
header:
|
||||
one: 有 1 个错误发生导致「%{model}」无法被保存。
|
||||
other: 有 %{count} 个错误发生导致「%{model}」无法被保存。
|
||||
helpers:
|
||||
select:
|
||||
prompt: 请选择
|
||||
submit:
|
||||
create: 新增%{model}
|
||||
submit: 储存%{model}
|
||||
update: 更新%{model}
|
||||
number:
|
||||
currency:
|
||||
format:
|
||||
delimiter: ","
|
||||
format: "%u %n"
|
||||
precision: 2
|
||||
separator: "."
|
||||
significant: false
|
||||
strip_insignificant_zeros: false
|
||||
unit: CN¥
|
||||
format:
|
||||
delimiter: ","
|
||||
precision: 3
|
||||
separator: "."
|
||||
significant: false
|
||||
strip_insignificant_zeros: false
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n %u"
|
||||
units:
|
||||
billion: 十亿
|
||||
million: 百万
|
||||
quadrillion: 千兆
|
||||
thousand: 千
|
||||
trillion: 兆
|
||||
unit: ''
|
||||
format:
|
||||
delimiter: ''
|
||||
precision: 1
|
||||
significant: false
|
||||
strip_insignificant_zeros: false
|
||||
storage_units:
|
||||
format: "%n %u"
|
||||
units:
|
||||
byte:
|
||||
one: Byte
|
||||
other: Bytes
|
||||
gb: GB
|
||||
kb: KB
|
||||
mb: MB
|
||||
tb: TB
|
||||
percentage:
|
||||
format:
|
||||
delimiter: ''
|
||||
precision:
|
||||
format:
|
||||
delimiter: ''
|
||||
support:
|
||||
array:
|
||||
last_word_connector: ", 和 "
|
||||
two_words_connector: " 和 "
|
||||
words_connector: ", "
|
||||
time:
|
||||
am: 上午
|
||||
formats:
|
||||
default: "%Y年%b%d日 %A %H:%M:%S"
|
||||
long: "%Y年%b%d日 %H:%M"
|
||||
short: "%b%d日 %H:%M"
|
||||
pm: 下午
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# Additional translations at https://github.com/plataformatec/devise/wiki/I18n
|
||||
|
||||
en:
|
||||
devise:
|
||||
confirmations:
|
||||
confirmed: "Your email address has been successfully confirmed."
|
||||
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
|
||||
send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
|
||||
failure:
|
||||
already_authenticated: "You are already signed in."
|
||||
inactive: "Your account is not activated yet."
|
||||
invalid: "Invalid %{authentication_keys} or password."
|
||||
locked: "Your account is locked."
|
||||
last_attempt: "You have one more attempt before your account is locked."
|
||||
not_found_in_database: "Invalid %{authentication_keys} or password."
|
||||
timeout: "Your session expired. Please sign in again to continue."
|
||||
unauthenticated: "You need to sign in or sign up before continuing."
|
||||
unconfirmed: "You have to confirm your email address before continuing."
|
||||
mailer:
|
||||
confirmation_instructions:
|
||||
subject: "Confirmation instructions"
|
||||
reset_password_instructions:
|
||||
subject: "Reset password instructions"
|
||||
unlock_instructions:
|
||||
subject: "Unlock instructions"
|
||||
omniauth_callbacks:
|
||||
failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
|
||||
success: "Successfully authenticated from %{kind} account."
|
||||
passwords:
|
||||
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
|
||||
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
|
||||
send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
|
||||
updated: "Your password has been changed successfully. You are now signed in."
|
||||
updated_not_active: "Your password has been changed successfully."
|
||||
registrations:
|
||||
destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
|
||||
signed_up: "Welcome! You have signed up successfully."
|
||||
signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
|
||||
signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
|
||||
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
|
||||
update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address."
|
||||
updated: "Your account has been updated successfully."
|
||||
sessions:
|
||||
signed_in: "Signed in successfully."
|
||||
signed_out: "Signed out successfully."
|
||||
already_signed_out: "Signed out successfully."
|
||||
unlocks:
|
||||
send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
|
||||
send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
|
||||
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
|
||||
errors:
|
||||
messages:
|
||||
already_confirmed: "was already confirmed, please try signing in"
|
||||
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
|
||||
expired: "has expired, please request a new one"
|
||||
not_found: "not found"
|
||||
not_locked: "was not locked"
|
||||
not_saved:
|
||||
one: "1 error prohibited this %{resource} from being saved:"
|
||||
other: "%{count} errors prohibited this %{resource} from being saved:"
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Chinese (China) translations for Devise(3.2.2)
|
||||
# by Kenrick-Zhou (https://github.com/Kenrick-Zhou)
|
||||
# https://gist.github.com/Kenrick-Zhou/7909822
|
||||
|
||||
zh-CN:
|
||||
devise:
|
||||
confirmations:
|
||||
confirmed: "您的帐号已经确认,您现在已登录。"
|
||||
send_instructions: "几分钟后,您将收到确认帐号的电子邮件。"
|
||||
send_paranoid_instructions: "如果您的邮箱存在于我们的数据库中,您将收到一封确认账号的邮件。"
|
||||
failure:
|
||||
already_authenticated: "您已经登录。"
|
||||
inactive: "您还没有激活帐户。"
|
||||
invalid: "邮箱或密码错误。"
|
||||
locked: "您的帐号已被锁定。"
|
||||
last_attempt: "您还有最后一次尝试机会,再次失败您的账号将被锁定。"
|
||||
not_found_in_database: "邮箱或密码错误。"
|
||||
timeout: "您已登录超时,请重新登录。"
|
||||
unauthenticated: "继续操作前请注册或者登录。"
|
||||
unconfirmed: "继续操作前请先确认您的帐号。"
|
||||
mailer:
|
||||
confirmation_instructions:
|
||||
subject: "来自ezcms的注册确认邮件"
|
||||
reset_password_instructions:
|
||||
subject: "重置密码信息"
|
||||
unlock_instructions:
|
||||
subject: "解锁信息"
|
||||
omniauth_callbacks:
|
||||
failure: "因为%{reason},所以您无法从%{kind}获得授权。"
|
||||
success: "成功地从%{kind}获得授权。"
|
||||
passwords:
|
||||
no_token: "这是密码重置页面,未重置邮件不得访问此页面。如果您是通过重置邮件而来的,请确保您访问的URL是完整的。"
|
||||
send_instructions: "几分钟后,您将收到重置密码的电子邮件。"
|
||||
send_paranoid_instructions: "如果您的邮箱存在于我们的数据库中,您将收到一封找回密码的邮件。"
|
||||
updated: "您的密码已修改成功,您现在已登录。"
|
||||
updated_not_active: "您的密码已修改成功。"
|
||||
registrations:
|
||||
destroyed: "再见!您的帐户已成功注销。我们希望很快可以再见到您。"
|
||||
signed_up: "欢迎您!您已注册成功。"
|
||||
signed_up_but_inactive: "您已注册,但尚未激活账号。"
|
||||
signed_up_but_locked: "您已注册,但账号被锁定了。"
|
||||
signed_up_but_unconfirmed: "一封带有确认链接的邮件已经发送至您的邮箱,请检查邮箱(包括垃圾邮箱),并点击该链接激活您的账号。"
|
||||
update_needs_confirmation: "信息更新成功,但我们需要染整您的新电子邮件地址,请检查邮箱(包括垃圾邮箱),并点击该链接激活您的账号。"
|
||||
updated: "帐号资料更新成功。"
|
||||
sessions:
|
||||
signed_in: "登录成功."
|
||||
signed_out: "退出成功."
|
||||
unlocks:
|
||||
send_instructions: "很快,您将收到一封解锁帐号的邮件。"
|
||||
send_paranoid_instructions: "如果您的邮箱存在于我们的数据库中,您将收到一封解锁账号的邮件。"
|
||||
unlocked: "您的帐号已成功解锁,您现在已登录。"
|
||||
errors:
|
||||
messages:
|
||||
already_confirmed: "已经确认,请重新登录。"
|
||||
confirmation_period_expired: "请在%{period}内确认注册, 请重新注册。"
|
||||
expired: "邮件确认已过期,请重新注册。"
|
||||
not_found: "没有找到。"
|
||||
not_locked: "未锁定。"
|
||||
not_saved:
|
||||
one: "因为1个错误导致此%{resource}保存失败:"
|
||||
other: "因为%{count}个错误导致此%{resource}保存失败:"
|
||||
|
|
@ -16,16 +16,6 @@
|
|||
#
|
||||
# This would use the information in config/locales/es.yml.
|
||||
#
|
||||
# The following keys must be escaped otherwise they will not be retrieved by
|
||||
# the default I18n backend:
|
||||
#
|
||||
# true, false, on, off, yes, no
|
||||
#
|
||||
# Instead, surround them with single quotes.
|
||||
#
|
||||
# en:
|
||||
# 'true': 'foo'
|
||||
#
|
||||
# To learn more, please read the Rails Internationalization guide
|
||||
# available at http://guides.rubyonrails.org/i18n.html.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
zh-CN:
|
||||
activerecord:
|
||||
models:
|
||||
order: 订单
|
||||
attributes:
|
||||
order:
|
||||
number: 订单号
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
zh-CN:
|
||||
activerecord:
|
||||
models:
|
||||
product: 商品
|
||||
attributes:
|
||||
product:
|
||||
name: 名称
|
||||
description: 描述
|
||||
price: 价格
|
||||
created_at: 添加日期
|
||||
updated_at: 更新日期
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
zh-CN:
|
||||
activerecord:
|
||||
models:
|
||||
user: 用户
|
||||
attributes:
|
||||
user:
|
||||
email: "邮箱"
|
||||
name: "用户名"
|
||||
password: "密码"
|
||||
password_confirmation: "确认密码"
|
||||
remember_me: "记住我"
|
||||
terms: "使用协议"
|
||||
current_password: "当前密码"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
zh-CN:
|
||||
activerecord:
|
||||
models:
|
||||
variant: 商品类型
|
||||
attributes:
|
||||
variant:
|
||||
price: 价格
|
||||
size: 大小
|
||||
created_at: 添加日期
|
||||
updated_at: 更新日期
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
en:
|
||||
flash:
|
||||
actions:
|
||||
create:
|
||||
notice: '%{resource_name} was successfully created.'
|
||||
# alert: '%{resource_name} could not be created.'
|
||||
update:
|
||||
notice: '%{resource_name} was successfully updated.'
|
||||
# alert: '%{resource_name} could not be updated.'
|
||||
destroy:
|
||||
notice: '%{resource_name} was successfully destroyed.'
|
||||
alert: '%{resource_name} could not be destroyed.'
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
en:
|
||||
flash:
|
||||
actions:
|
||||
create:
|
||||
notice: '%{resource_name} was successfully created.'
|
||||
# alert: '%{resource_name} could not be created.'
|
||||
update:
|
||||
notice: '%{resource_name} was successfully updated.'
|
||||
# alert: '%{resource_name} could not be updated.'
|
||||
destroy:
|
||||
notice: '%{resource_name} was successfully destroyed.'
|
||||
alert: '%{resource_name} could not be destroyed.'
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
zh-CN:
|
||||
flash:
|
||||
actions:
|
||||
create:
|
||||
notice: '%{resource_name} 创建成功'
|
||||
alert: '%{resource_name} 创建失败'
|
||||
update:
|
||||
notice: '%{resource_name} 更新成功'
|
||||
alert: '%{resource_name} 更新失败'
|
||||
destroy:
|
||||
notice: '%{resource_name} 删除成功'
|
||||
alert: '%{resource_name} 删除失败'
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
zh-CN:
|
||||
will_paginate:
|
||||
next_label: 下一页
|
||||
previous_label: '上一页'
|
||||
page_gap: …
|
||||
page_entries_info:
|
||||
single_page:
|
||||
zero: '暂无记录'
|
||||
one: '1条记录'
|
||||
other: '共 %{count} 条记录'
|
||||
single_page_html:
|
||||
zero: '暂无记录'
|
||||
one: '1 条记录'
|
||||
other: '所有共 %{count} 条记录'
|
||||
|
||||
multi_page: '第 %{from} 至第 %{to} 条记录(共 %{count} 条记录)'
|
||||
multi_page_html: '第 %{from} 至第 %{to} 条记录(共 %{count} 条记录)'
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
zh-CN:
|
||||
hello: "你好"
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
Rails.application.routes.draw do
|
||||
require 'sidekiq/web'
|
||||
mount Sidekiq::Web => '/sidekiq'
|
||||
resources :orders
|
||||
devise_for :users
|
||||
get '/',to: 'products#index'
|
||||
get 'home/:id', to: 'home#welcome', constraints: { id: /[A-Z]\d{5}/ }
|
||||
|
|
@ -15,6 +18,12 @@ Rails.application.routes.draw do
|
|||
post :buy
|
||||
end
|
||||
end
|
||||
get "/cart", to: 'checkout#cart', as: :cart
|
||||
post "/checkout", to: 'checkout#checkout', as: :checkout
|
||||
get "/address", to: 'checkout#address', as: :address
|
||||
post "/confirm", to: 'checkout#confirm', as: :confirm
|
||||
post "/:number/pay", to: 'checkout#pay', as: :pay
|
||||
get "/:number/notify", to: 'checkout#notify', as: :notify
|
||||
namespace :admin do
|
||||
resources :products
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
# Options here can still be overridden by cmd line args.
|
||||
# sidekiq -C sidekiq.yml
|
||||
---
|
||||
:logfile: ./log/sidekiq.log
|
||||
:verbose: false
|
||||
:concurrency: 5
|
||||
:queues:
|
||||
- [a, 5]
|
||||
- [b, 3]
|
||||
- [c, 2]
|
||||
- [default, 3]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
class CreateCartItems < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :cart_items do |t|
|
||||
t.references :user
|
||||
t.references :variant
|
||||
t.integer :quantity, default: 0
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
class CreateJoinTable < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_join_table :users, :variants do |t|
|
||||
# t.index [:user_id, :variant_id]
|
||||
t.index [:variant_id, :user_id], unique: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
class CreateCatalogs < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :catalogs do |t|
|
||||
t.string :name
|
||||
t.boolean :parent
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class AddOnHandToVariants < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :variants, :on_hand, :integer
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class AddRoleToUsers < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :users, :role, :string
|
||||
end
|
||||
end
|
||||
42
db/schema.rb
42
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2022_05_18_100755) do
|
||||
ActiveRecord::Schema.define(version: 2022_05_19_073140) do
|
||||
|
||||
create_table "addresses", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.bigint "user_id"
|
||||
|
|
@ -26,6 +26,35 @@ ActiveRecord::Schema.define(version: 2022_05_18_100755) do
|
|||
t.index ["user_id"], name: "index_addresses_on_user_id"
|
||||
end
|
||||
|
||||
create_table "cart_items", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.bigint "user_id"
|
||||
t.bigint "variant_id"
|
||||
t.integer "quantity", default: 0
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["user_id"], name: "index_cart_items_on_user_id"
|
||||
t.index ["variant_id"], name: "index_cart_items_on_variant_id"
|
||||
end
|
||||
|
||||
create_table "catalogs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.boolean "parent"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "parent_catalog_id"
|
||||
t.index ["parent_catalog_id"], name: "catalogs_catalogs_id_fk"
|
||||
end
|
||||
|
||||
create_table "line_items", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.bigint "order_id"
|
||||
t.bigint "variant_id"
|
||||
t.integer "quantity"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["order_id"], name: "index_line_items_on_order_id"
|
||||
t.index ["variant_id"], name: "index_line_items_on_variant_id"
|
||||
end
|
||||
|
||||
create_table "orders", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.bigint "user_id"
|
||||
t.string "number"
|
||||
|
|
@ -59,18 +88,29 @@ ActiveRecord::Schema.define(version: 2022_05_18_100755) do
|
|||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "orders_count"
|
||||
t.string "role"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
end
|
||||
|
||||
create_table "users_variants", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.bigint "user_id", null: false
|
||||
t.bigint "variant_id", null: false
|
||||
t.index ["variant_id", "user_id"], name: "index_users_variants_on_variant_id_and_user_id", unique: true
|
||||
end
|
||||
|
||||
create_table "variants", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t|
|
||||
t.integer "product_id"
|
||||
t.decimal "price", precision: 8, scale: 2
|
||||
t.string "size"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "on_hand"
|
||||
end
|
||||
|
||||
add_foreign_key "addresses", "users"
|
||||
add_foreign_key "catalogs", "catalogs", column: "parent_catalog_id", name: "catalogs_catalogs_id_fk"
|
||||
add_foreign_key "line_items", "orders"
|
||||
add_foreign_key "line_items", "variants"
|
||||
add_foreign_key "orders", "users"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
class ApplicationResponder < ActionController::Responder
|
||||
include Responders::FlashResponder
|
||||
include Responders::HttpCacheResponder
|
||||
|
||||
# Redirects resources to the collection path (index action) instead
|
||||
# of the resource path (show action) for POST/PUT/DELETE requests.
|
||||
# include Responders::CollectionResponder
|
||||
end
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
require 'rails_helper'
|
||||
|
||||
|
||||
RSpec.describe ProductsController, type: :controller do
|
||||
let(:valid_attributes) {
|
||||
skip("Add a hash of attributes valid for your model")
|
||||
}
|
||||
|
||||
let(:invalid_attributes) {
|
||||
skip("Add a hash of attributes invalid for your model")
|
||||
}
|
||||
let(:valid_session) { {} }
|
||||
|
||||
login_user
|
||||
|
||||
describe "GET #index" do
|
||||
it "assigns all products as @products" do
|
||||
product = Product.create! valid_attributes
|
||||
get :index, {}, valid_session
|
||||
expect(assigns(:products)).to eq([product])
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #show" do
|
||||
it "assigns the requested product as @product" do
|
||||
product = Product.create! valid_attributes
|
||||
get :show, {:id => product.to_param}, valid_session
|
||||
expect(assigns(:product)).to eq(product)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #new" do
|
||||
it "assigns a new product as @product" do
|
||||
get :new, {}, valid_session
|
||||
expect(assigns(:product)).to be_a_new(Product)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #edit" do
|
||||
it "assigns the requested product as @product" do
|
||||
product = Product.create! valid_attributes
|
||||
get :edit, {:id => product.to_param}, valid_session
|
||||
expect(assigns(:product)).to eq(product)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST #create" do
|
||||
context "with valid params" do
|
||||
it "creates a new Product" do
|
||||
expect {
|
||||
post :create, {:product => valid_attributes}, valid_session
|
||||
}.to change(Product, :count).by(1)
|
||||
end
|
||||
|
||||
it "assigns a newly created product as @product" do
|
||||
post :create, {:product => valid_attributes}, valid_session
|
||||
expect(assigns(:product)).to be_a(Product)
|
||||
expect(assigns(:product)).to be_persisted
|
||||
end
|
||||
|
||||
it "redirects to the created product" do
|
||||
post :create, {:product => valid_attributes}, valid_session
|
||||
expect(response).to redirect_to(Product.last)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid params" do
|
||||
it "assigns a newly created but unsaved product as @product" do
|
||||
post :create, {:product => invalid_attributes}, valid_session
|
||||
expect(assigns(:product)).to be_a_new(Product)
|
||||
end
|
||||
|
||||
it "re-renders the 'new' template" do
|
||||
post :create, {:product => invalid_attributes}, valid_session
|
||||
expect(response).to render_template("new")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT #update" do
|
||||
context "with valid params" do
|
||||
let(:new_attributes) {
|
||||
skip("Add a hash of attributes valid for your model")
|
||||
}
|
||||
|
||||
it "updates the requested product" do
|
||||
product = Product.create! valid_attributes
|
||||
put :update, {:id => product.to_param, :product => new_attributes}, valid_session
|
||||
product.reload
|
||||
skip("Add assertions for updated state")
|
||||
end
|
||||
|
||||
it "assigns the requested product as @product" do
|
||||
product = Product.create! valid_attributes
|
||||
put :update, {:id => product.to_param, :product => valid_attributes}, valid_session
|
||||
expect(assigns(:product)).to eq(product)
|
||||
end
|
||||
|
||||
it "redirects to the product" do
|
||||
product = Product.create! valid_attributes
|
||||
put :update, {:id => product.to_param, :product => valid_attributes}, valid_session
|
||||
expect(response).to redirect_to(product)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid params" do
|
||||
it "assigns the product as @product" do
|
||||
product = Product.create! valid_attributes
|
||||
put :update, {:id => product.to_param, :product => invalid_attributes}, valid_session
|
||||
expect(assigns(:product)).to eq(product)
|
||||
end
|
||||
|
||||
it "re-renders the 'edit' template" do
|
||||
product = Product.create! valid_attributes
|
||||
put :update, {:id => product.to_param, :product => invalid_attributes}, valid_session
|
||||
expect(response).to render_template("edit")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE #destroy" do
|
||||
it "destroys the requested product" do
|
||||
product = Product.create! valid_attributes
|
||||
expect {
|
||||
delete :destroy, {:id => product.to_param}, valid_session
|
||||
}.to change(Product, :count).by(-1)
|
||||
end
|
||||
|
||||
it "redirects to the products list" do
|
||||
product = Product.create! valid_attributes
|
||||
delete :destroy, {:id => product.to_param}, valid_session
|
||||
expect(response).to redirect_to(products_url)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe OrderCreateJob, type: :job do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe OrderMailer, type: :mailer do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Preview all emails at http://localhost:3000/rails/mailers/order_mailer
|
||||
class OrderMailerPreview < ActionMailer::Preview
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Catalog, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe User, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
it { should have_many(:orders) }
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue