diff --git a/app/views/order_mailer/confirm_email.html.erb b/app/views/order_mailer/confirm_email.html.erb
new file mode 100644
index 0000000..9d15115
--- /dev/null
+++ b/app/views/order_mailer/confirm_email.html.erb
@@ -0,0 +1,4 @@
+
你好, <%= @user.email %>
+
+ 您的订单 <%= @order.number %> 已经确认。
+
diff --git a/app/views/order_mailer/confirm_email.text.erb b/app/views/order_mailer/confirm_email.text.erb
new file mode 100644
index 0000000..6066ee7
--- /dev/null
+++ b/app/views/order_mailer/confirm_email.text.erb
@@ -0,0 +1 @@
+你好, <%= @user.email %>,您的订单 <%= @order.number %> 已经确认。
diff --git a/app/views/orders/_form.html.erb b/app/views/orders/_form.html.erb
new file mode 100644
index 0000000..15cdbdb
--- /dev/null
+++ b/app/views/orders/_form.html.erb
@@ -0,0 +1,17 @@
+<%= form_for(@order) do |f| %>
+ <% if @order.errors.any? %>
+
+
<%= pluralize(@order.errors.count, "error") %> prohibited this order from being saved:
+
+
+ <% @order.errors.full_messages.each do |message| %>
+ - <%= message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= f.submit %>
+
+<% end %>
diff --git a/app/views/orders/edit.html.erb b/app/views/orders/edit.html.erb
new file mode 100644
index 0000000..c621bd2
--- /dev/null
+++ b/app/views/orders/edit.html.erb
@@ -0,0 +1,6 @@
+
Editing Order
+
+<%= render 'form' %>
+
+<%= link_to 'Show', @order %> |
+<%= link_to 'Back', orders_path %>
diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb
new file mode 100644
index 0000000..ba467dd
--- /dev/null
+++ b/app/views/orders/index.html.erb
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+ | 订单 |
+ 总额 |
+ 状态 |
+ 创建时间 |
+ |
+
+
+ <% @orders.each do |order| %>
+
+ | <%= link_to order.number, order %> |
+ <%= number_to_currency order.line_items.map{|i| i.quantity * i.variant_price}.sum %> |
+ <%= order.payment_state %> |
+ <%= order.created_at.to_date %> |
+ <%= link_to "支付", pay_path(number: order.number), method: :post if order.payment_state == "confirm" %> |
+
+ <% end %>
+
+
+
+
+ <%= page_entries_info @orders %>
+
+<%= will_paginate @orders, renderer: BootstrapPagination::Rails %>
+
diff --git a/app/views/orders/index.json.jbuilder b/app/views/orders/index.json.jbuilder
new file mode 100644
index 0000000..217c653
--- /dev/null
+++ b/app/views/orders/index.json.jbuilder
@@ -0,0 +1,4 @@
+json.array!(@orders) do |order|
+ json.extract! order, :id
+ json.url order_url(order, format: :json)
+end
diff --git a/app/views/orders/new.html.erb b/app/views/orders/new.html.erb
new file mode 100644
index 0000000..a7214a0
--- /dev/null
+++ b/app/views/orders/new.html.erb
@@ -0,0 +1,5 @@
+
New Order
+
+<%= render 'form' %>
+
+<%= link_to 'Back', orders_path %>
diff --git a/app/views/orders/show.html.erb b/app/views/orders/show.html.erb
new file mode 100644
index 0000000..09400d2
--- /dev/null
+++ b/app/views/orders/show.html.erb
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ | 商品 |
+ 大小 |
+ 价格 |
+ 数量 |
+ 合计 |
+
+
+ <% @order.line_items.each do |item| %>
+
+ | <%= link_to item.variant.product_name, product_path(item.variant.product_id) %> |
+ <%= item.variant.size %> |
+ <%= item.variant_price %> |
+ <%= item.quantity %> |
+ <%= number_to_currency item.quantity * item.variant_price %> |
+
+ <% end %>
+
+
+ | <%= number_to_currency @order.line_items.map{|item| item.quantity * item.variant_price}.sum %> |
+
+
+
+
+
+
diff --git a/app/views/orders/show.json.jbuilder b/app/views/orders/show.json.jbuilder
new file mode 100644
index 0000000..e7be84b
--- /dev/null
+++ b/app/views/orders/show.json.jbuilder
@@ -0,0 +1 @@
+json.extract! @order, :id, :created_at, :updated_at
diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb
index f16a482..276d411 100644
--- a/app/views/products/show.html.erb
+++ b/app/views/products/show.html.erb
@@ -11,7 +11,42 @@
<%= model_class.human_attribute_name(:description) %>:
<%= @product.description %>
-
+<%= form_tag buy_product_path(@product) do %>
+
+
+
+
选择您想购买的商品
+
+ <% @product.variants.each_with_index do |variant, index| %>
+ -
+
+
+ <% end%>
+
+
+
+
+
+
+ <%= number_field_tag :quantity, 1, :class => 'form-control', :min => 1, style: "text-align: center;" %>
+
+ <%= button_tag :class => 'btn btn-success', :type => :submit do %>
+ 添加到购物车
+ <% end %>
+
+
+
+
+
+
+<% 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")),
diff --git a/config/application.rb b/config/application.rb
index c2c7b00..8e11054 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -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
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 2ef037a..87cc745 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -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
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
new file mode 100644
index 0000000..566a896
--- /dev/null
+++ b/config/initializers/sidekiq.rb
@@ -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
diff --git a/config/locales/bootstrap/zh-CN.yml b/config/locales/bootstrap/zh-CN.yml
new file mode 100644
index 0000000..750b7d1
--- /dev/null
+++ b/config/locales/bootstrap/zh-CN.yml
@@ -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}"
diff --git a/config/locales/defaults/zh-CN.yml b/config/locales/defaults/zh-CN.yml
new file mode 100644
index 0000000..3d4f9d6
--- /dev/null
+++ b/config/locales/defaults/zh-CN.yml
@@ -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: 下午
diff --git a/config/locales/devise/en.yml b/config/locales/devise/en.yml
new file mode 100644
index 0000000..26a10f2
--- /dev/null
+++ b/config/locales/devise/en.yml
@@ -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:"
diff --git a/config/locales/devise/zh-CN.yml b/config/locales/devise/zh-CN.yml
new file mode 100644
index 0000000..166c4d2
--- /dev/null
+++ b/config/locales/devise/zh-CN.yml
@@ -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}保存失败:"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index decc5a8..0653957 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -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.
diff --git a/config/locales/models/order/zh-CN.yml b/config/locales/models/order/zh-CN.yml
new file mode 100644
index 0000000..373fe2a
--- /dev/null
+++ b/config/locales/models/order/zh-CN.yml
@@ -0,0 +1,7 @@
+zh-CN:
+ activerecord:
+ models:
+ order: 订单
+ attributes:
+ order:
+ number: 订单号
diff --git a/config/locales/models/product/zh-CN.yml b/config/locales/models/product/zh-CN.yml
new file mode 100644
index 0000000..cc54b8a
--- /dev/null
+++ b/config/locales/models/product/zh-CN.yml
@@ -0,0 +1,11 @@
+zh-CN:
+ activerecord:
+ models:
+ product: 商品
+ attributes:
+ product:
+ name: 名称
+ description: 描述
+ price: 价格
+ created_at: 添加日期
+ updated_at: 更新日期
diff --git a/config/locales/models/user/zh-CN.yml b/config/locales/models/user/zh-CN.yml
new file mode 100644
index 0000000..cb0bca2
--- /dev/null
+++ b/config/locales/models/user/zh-CN.yml
@@ -0,0 +1,13 @@
+zh-CN:
+ activerecord:
+ models:
+ user: 用户
+ attributes:
+ user:
+ email: "邮箱"
+ name: "用户名"
+ password: "密码"
+ password_confirmation: "确认密码"
+ remember_me: "记住我"
+ terms: "使用协议"
+ current_password: "当前密码"
diff --git a/config/locales/models/variant/zh-CN.yml b/config/locales/models/variant/zh-CN.yml
new file mode 100644
index 0000000..75a7623
--- /dev/null
+++ b/config/locales/models/variant/zh-CN.yml
@@ -0,0 +1,10 @@
+zh-CN:
+ activerecord:
+ models:
+ variant: 商品类型
+ attributes:
+ variant:
+ price: 价格
+ size: 大小
+ created_at: 添加日期
+ updated_at: 更新日期
diff --git a/config/locales/responders.en.yml b/config/locales/responders.en.yml
new file mode 100644
index 0000000..c3e147a
--- /dev/null
+++ b/config/locales/responders.en.yml
@@ -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.'
diff --git a/config/locales/responders/en.yml b/config/locales/responders/en.yml
new file mode 100644
index 0000000..c3e147a
--- /dev/null
+++ b/config/locales/responders/en.yml
@@ -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.'
diff --git a/config/locales/responders/zh-CN.yml b/config/locales/responders/zh-CN.yml
new file mode 100644
index 0000000..6b3004f
--- /dev/null
+++ b/config/locales/responders/zh-CN.yml
@@ -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} 删除失败'
diff --git a/config/locales/will_paginate/zh-CN.yml b/config/locales/will_paginate/zh-CN.yml
new file mode 100644
index 0000000..22c4964
--- /dev/null
+++ b/config/locales/will_paginate/zh-CN.yml
@@ -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} 条记录)'
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
new file mode 100644
index 0000000..7750d07
--- /dev/null
+++ b/config/locales/zh-CN.yml
@@ -0,0 +1,2 @@
+zh-CN:
+ hello: "你好"
diff --git a/config/routes.rb b/config/routes.rb
index 1fe2f04..238883d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -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
diff --git a/config/sidekiq.yml b/config/sidekiq.yml
new file mode 100644
index 0000000..01c6c27
--- /dev/null
+++ b/config/sidekiq.yml
@@ -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]
diff --git a/db/migrate/20150628073804_create_cart_items.rb b/db/migrate/20150628073804_create_cart_items.rb
new file mode 100644
index 0000000..a486f5b
--- /dev/null
+++ b/db/migrate/20150628073804_create_cart_items.rb
@@ -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
diff --git a/db/migrate/20220519020037_create_join_table.rb b/db/migrate/20220519020037_create_join_table.rb
new file mode 100644
index 0000000..1a92bb6
--- /dev/null
+++ b/db/migrate/20220519020037_create_join_table.rb
@@ -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
diff --git a/db/migrate/20220519021031_create_catalogs.rb b/db/migrate/20220519021031_create_catalogs.rb
new file mode 100644
index 0000000..a79f0ca
--- /dev/null
+++ b/db/migrate/20220519021031_create_catalogs.rb
@@ -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
diff --git a/db/migrate/20220519054151_add_on_hand_to_variants.rb b/db/migrate/20220519054151_add_on_hand_to_variants.rb
new file mode 100644
index 0000000..1a12260
--- /dev/null
+++ b/db/migrate/20220519054151_add_on_hand_to_variants.rb
@@ -0,0 +1,5 @@
+class AddOnHandToVariants < ActiveRecord::Migration[5.2]
+ def change
+ add_column :variants, :on_hand, :integer
+ end
+end
diff --git a/db/migrate/20220519073140_add_role_to_users.rb b/db/migrate/20220519073140_add_role_to_users.rb
new file mode 100644
index 0000000..567ae13
--- /dev/null
+++ b/db/migrate/20220519073140_add_role_to_users.rb
@@ -0,0 +1,5 @@
+class AddRoleToUsers < ActiveRecord::Migration[5.2]
+ def change
+ add_column :users, :role, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 77bffa1..6f52f2f 100644
--- a/db/schema.rb
+++ b/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
diff --git a/lib/application_responder.rb b/lib/application_responder.rb
new file mode 100644
index 0000000..cc3e588
--- /dev/null
+++ b/lib/application_responder.rb
@@ -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
diff --git a/spec/controllers/product_controller_spec.rb b/spec/controllers/product_controller_spec.rb
new file mode 100644
index 0000000..1cdd16f
--- /dev/null
+++ b/spec/controllers/product_controller_spec.rb
@@ -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
\ No newline at end of file
diff --git a/spec/jobs/order_create_job_spec.rb b/spec/jobs/order_create_job_spec.rb
new file mode 100644
index 0000000..b18afdb
--- /dev/null
+++ b/spec/jobs/order_create_job_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe OrderCreateJob, type: :job do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/mailers/order_mailer_spec.rb b/spec/mailers/order_mailer_spec.rb
new file mode 100644
index 0000000..47e2230
--- /dev/null
+++ b/spec/mailers/order_mailer_spec.rb
@@ -0,0 +1,5 @@
+require "rails_helper"
+
+RSpec.describe OrderMailer, type: :mailer do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/mailers/previews/order_mailer_preview.rb b/spec/mailers/previews/order_mailer_preview.rb
new file mode 100644
index 0000000..7be457e
--- /dev/null
+++ b/spec/mailers/previews/order_mailer_preview.rb
@@ -0,0 +1,4 @@
+# Preview all emails at http://localhost:3000/rails/mailers/order_mailer
+class OrderMailerPreview < ActionMailer::Preview
+
+end
diff --git a/spec/models/catalog_spec.rb b/spec/models/catalog_spec.rb
new file mode 100644
index 0000000..09e23f5
--- /dev/null
+++ b/spec/models/catalog_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Catalog, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 47a31bb..bdab0a8 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -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