Commit cbf6bb70 by ivan Lan

Add JsonColumn & Improve spec & Add Shotengai::Model

parent ef84b6d6
...@@ -101,7 +101,7 @@ RSpec.describe "#{namespace}/product_snapshots", type: :request, capture_example ...@@ -101,7 +101,7 @@ RSpec.describe "#{namespace}/product_snapshots", type: :request, capture_example
produces 'application/json' produces 'application/json'
consumes 'application/json' consumes 'application/json'
response(200, description: 'successful') do response(200, description: 'successful') do
it { expect(JSON(response.body)['product_status_zh']).to eq('上架'), 'check product_status_zh' } it { expect(JSON(response.body)['product_status_zh']).to eq('上架'), 'check product_status_zh' }
end end
end end
......
...@@ -2,8 +2,7 @@ require 'swagger_helper' ...@@ -2,8 +2,7 @@ require 'swagger_helper'
namespace = '<%= @namespace %>' namespace = '<%= @namespace %>'
RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, tags: ["#{namespace} API", "product"] do RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, tags: ["#{namespace} API", "product"] do
before do before do
@merchant = Merchant.register "merchant", "password" @merchant = create(:merchant)
@auth_token = Merchant.login "merchant", "password"
class Catalog < Shotengai::Catalog; end class Catalog < Shotengai::Catalog; end
@clothes = Catalog.create!(name: '衣服') @clothes = Catalog.create!(name: '衣服')
...@@ -17,8 +16,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -17,8 +16,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
path "/#{namespace}/products" do path "/#{namespace}/products" do
get(summary: '商家 商品列表') do get(summary: '商家 商品列表') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
parameter :page, in: :query, type: :string parameter :page, in: :query, type: :string
parameter :per_page, in: :query, type: :string parameter :per_page, in: :query, type: :string
...@@ -41,15 +42,17 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -41,15 +42,17 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
end end
response(200, description: 'filter by status') do response(200, description: 'filter by status') do
before { @product_1.put_on_shelf! } before { @product_1.sold_out! }
let(:status) { 'on_sale' } let(:status) { 'not_on' }
it { expect(JSON.parse(response.body)['products'].count).to eq(1) } it { expect(JSON.parse(response.body)['products'].count).to eq(1) }
end end
end end
post(summary: '管理员新建商品') do post(summary: '管理员新建商品') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
parameter :product, in: :body, schema: { parameter :product, in: :body, schema: {
type: :object, properties: { type: :object, properties: {
...@@ -116,15 +119,17 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -116,15 +119,17 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
parameter :ids, in: :query, type: :string parameter :ids, in: :query, type: :string
parameter :event, in: :query, type: :string parameter :event, in: :query, type: :string
let(:ids) { @products.first(2).map(&:id) } let(:ids) { @products.first(2).map(&:id) }
let(:event) { 'put_on_shelf' } let(:event) { 'sold_out' }
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
produces 'application/json' produces 'application/json'
consumes 'application/json' consumes 'application/json'
response(200, description: 'successful') do response(200, description: 'successful') do
it { it {
expect(Product.where(status: 'on_sale').map(&:id).sort).to eq(@products.first(2).map(&:id).sort) expect(Product.where(manager: @merchant).not_on.map(&:id).sort).to eq(@products.first(2).map(&:id).sort)
} }
end end
end end
...@@ -135,8 +140,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -135,8 +140,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
let(:id) { @product_1.id } let(:id) { @product_1.id }
get(summary: '商户 商品详情') do get(summary: '商户 商品详情') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
produces 'application/json' produces 'application/json'
consumes 'application/json' consumes 'application/json'
response(200, description: 'successful') do response(200, description: 'successful') do
...@@ -146,8 +153,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -146,8 +153,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
end end
end end
patch(summary: 'update product') do patch(summary: 'update product') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
produces 'application/json' produces 'application/json'
consumes 'application/json' consumes 'application/json'
...@@ -195,8 +204,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -195,8 +204,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
end end
delete(summary: 'delete product') do delete(summary: 'delete product') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
produces 'application/json' produces 'application/json'
consumes 'application/json' consumes 'application/json'
...@@ -210,13 +221,15 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -210,13 +221,15 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
let(:id) { @product_1.id } let(:id) { @product_1.id }
post(summary: '商户 上架商品') do post(summary: '商户 上架商品') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
produces 'application/json' produces 'application/json'
consumes 'application/json' consumes 'application/json'
response(200, description: 'successful') do response(200, description: 'successful') do
# it { p response.body } before { @product_1.sold_out! }
end end
end end
end end
...@@ -226,8 +239,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true, ...@@ -226,8 +239,10 @@ RSpec.describe "#{namespace}/products", type: :request, capture_examples: true,
let(:id) { @product_1.id } let(:id) { @product_1.id }
post(summary: '商户 下架商品') do post(summary: '商户 下架商品') do
parameter 'Merchant-Token', in: :header, type: :string parameter :manager_type, in: :query, type: :string
let('Merchant-Token') { @auth_token.token } parameter :manager_id, in: :query, type: :integer
let(:manager_id) { @merchant.id }
let(:manager_type) { @merchant.class.name }
before { @product_1.update!(status: 'on_sale') } before { @product_1.update!(status: 'on_sale') }
produces 'application/json' produces 'application/json'
......
module Shotengai
module JsonColumn
extend ActiveSupport::Concern
included do
end
class_methods do
def hash_columns *columns
columns.each do |column|
define_method("#{column}_input=") do |val|
val = val.map{ |h| { h[:key] => h[:val] } }.reduce(&:merge)
write_attribute(column, val)
end
define_method("#{column}_output") do
read_attribute(column).map {|key, val| { key: key, val: val } }
end
end
end
end
end
end
\ No newline at end of file
module Shotengai
class Model < ApplicationRecord
self.abstract_class = true
include Shotengai::JsonColumn
end
end
\ No newline at end of file
...@@ -29,7 +29,7 @@ module Shotengai ...@@ -29,7 +29,7 @@ module Shotengai
# index_shotengai_orders_on_type (type) # index_shotengai_orders_on_type (type)
# #
class Order < ActiveRecord::Base class Order < Shotengai::Model
self.table_name = 'shotengai_orders' self.table_name = 'shotengai_orders'
belongs_to :buyer, polymorphic: true, optional: true#, touch: true belongs_to :buyer, polymorphic: true, optional: true#, touch: true
...@@ -38,6 +38,8 @@ module Shotengai ...@@ -38,6 +38,8 @@ module Shotengai
after_create :set_seq after_create :set_seq
hash_columns :spec, :meta
include AASM_DLC include AASM_DLC
aasm column: :status do aasm column: :status do
state :unpaid, initial: true state :unpaid, initial: true
......
...@@ -26,10 +26,12 @@ module Shotengai ...@@ -26,10 +26,12 @@ module Shotengai
# index_shotengai_products_on_type (type) # index_shotengai_products_on_type (type)
# #
class Shotengai::Product < ActiveRecord::Base class Product < Shotengai::Model
require 'acts-as-taggable-on' require 'acts-as-taggable-on'
self.table_name = 'shotengai_products' self.table_name = 'shotengai_products'
hash_columns :spec, :meta, :detail
belongs_to :manager, polymorphic: true, optional: true#, touch: true belongs_to :manager, polymorphic: true, optional: true#, touch: true
validate :check_spec, if: :spec validate :check_spec, if: :spec
...@@ -76,15 +78,6 @@ module Shotengai ...@@ -76,15 +78,6 @@ module Shotengai
super || {} super || {}
end end
def spec_output
spec.map { |key, val|
{
key: key,
val: val,
}
}
end
class << self class << self
def series_class def series_class
Shotengai::Series Shotengai::Series
......
...@@ -21,7 +21,7 @@ module Shotengai ...@@ -21,7 +21,7 @@ module Shotengai
# index_shotengai_series_on_type (type) # index_shotengai_series_on_type (type)
# #
class Series < ActiveRecord::Base class Series < Shotengai::Model
self.table_name = 'shotengai_series' self.table_name = 'shotengai_series'
validates_presence_of :spec validates_presence_of :spec
validate :check_spec_value validate :check_spec_value
...@@ -29,6 +29,8 @@ module Shotengai ...@@ -29,6 +29,8 @@ module Shotengai
validate :uniq_spec validate :uniq_spec
validate :validate_stock validate :validate_stock
hash_columns :spec, :meta
delegate :title, :detail, :banners, :cover_image, :status, :status_zh, :manager, to: :product delegate :title, :detail, :banners, :cover_image, :status, :status_zh, :manager, to: :product
scope :alive, -> { where.not(aasm_state: 'deleted') } scope :alive, -> { where.not(aasm_state: 'deleted') }
......
...@@ -30,15 +30,17 @@ module Shotengai ...@@ -30,15 +30,17 @@ module Shotengai
# index_shotengai_snapshots_on_type (type) # index_shotengai_snapshots_on_type (type)
# #
class Snapshot < ActiveRecord::Base class Snapshot < Shotengai::Model
self.table_name = 'shotengai_snapshots' self.table_name = 'shotengai_snapshots'
validate :check_spec, if: :spec validate :check_spec, if: :spec
validates :count, numericality: { only_integer: true, greater_than: 0 } validates :count, numericality: { only_integer: true, greater_than: 0 }
hash_columns :spec, :meta, :detail
validate :cannot_edit, if: :order_was_paid validate :cannot_edit, if: :order_was_paid
before_destroy :cannot_edit, if: :order_was_paid before_destroy :cannot_edit, if: :order_was_paid
validate :cannot_edit_or_create, if: :already_disable? validate :cannot_edit_or_create, if: :already_disable
belongs_to :shotengai_order, foreign_key: :shotengai_order_id, belongs_to :shotengai_order, foreign_key: :shotengai_order_id,
class_name: 'Shotengai::Order', optional: true#, touch: true class_name: 'Shotengai::Order', optional: true#, touch: true
...@@ -93,6 +95,10 @@ module Shotengai ...@@ -93,6 +95,10 @@ module Shotengai
super || series.manager super || series.manager
end end
def product
series.product
end
# 订单支付后 存储当时信息快照 # 订单支付后 存储当时信息快照
def copy_info def copy_info
# cut_stock # cut_stock
...@@ -104,7 +110,7 @@ module Shotengai ...@@ -104,7 +110,7 @@ module Shotengai
banners: series.banners, banners: series.banners,
cover_image: series.cover_image, cover_image: series.cover_image,
detail: series.detail, detail: series.detail,
meta: (series.product.meta || {} ).merge(series.meta || {}) meta: (product.meta || {} ).merge(series.meta || {})
) )
end end
...@@ -153,7 +159,7 @@ module Shotengai ...@@ -153,7 +159,7 @@ module Shotengai
end end
def cannot_edit_or_create def cannot_edit_or_create
error.add(:id, '商品已下架,无法购买。') errors.add(:id, '商品已下架,无法购买。')
end end
def set_manager def set_manager
......
...@@ -26,9 +26,9 @@ ...@@ -26,9 +26,9 @@
# #
FactoryGirl.define do FactoryGirl.define do
factory :test_good, class: 'TestGood' do factory :product, class: 'Product' do
title 'Test Product Title' title 'Test Product Title'
# status status 'on_sale'
spec { spec {
{ {
"颜色" => ["黑色", "红色", "白色"], "颜色" => ["黑色", "红色", "白色"],
...@@ -39,36 +39,7 @@ FactoryGirl.define do ...@@ -39,36 +39,7 @@ FactoryGirl.define do
need_express true need_express true
# need_time_attr true # need_time_attr true
cover_image 'cover_image.image' cover_image 'cover_image.image'
banners { [ 'image1', 'image2' ] } banners { [ 'image1', 'iamge2' ] }
detail {
{
"使用说明" => "xxxxxxxx...",
"产品参数" => "参数 参数..."
}
}
# type "
meta {
{
"meta1" => "111",
"meta2" => "222",
}
}
end
factory :other_good, class: 'OtherGood' do
title 'Test Product Title'
# status
spec {
{
"颜色" => ["黑色", "红色", "白色"],
"大小" => ["S", "M", "L"],
}
}
# default_series_id ''
need_express true
# need_time_attr true
cover_image 'cover_image.image'
banners { [ 'image1', 'image2' ] }
detail { detail {
{ {
"使用说明" => "xxxxxxxx...", "使用说明" => "xxxxxxxx...",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment