database-archievement-theory

database-archievement-theory

Charles Lv7

Tradereads系统实现总结报告

一、实现环境

1.1 前端实现

1.1.1 体系结构

前端: vite+vue+js

前端使用 vite + vue3 + javascript作为基本框架进行web开发,采用了响应式布局,良好适配不同分辨率的屏幕,具有美观性;采用了vue的单文件组件格式,使用template进行基本布局,JavaScript为网页处理动态事件,使用tailwind css和element plus美化网页布局,实现网页布局和样式的一致性;基于VueRouter实现了前端路由跳转,无需每次从后端获取html页面,只需要通过api从后端获取少量数据,减少了IO开销,提高了网页速度;利用Pinia插件来管理全局状态,以便在整个应用程序中共享数据和状态; 集成ECharts库,以便在应用程序中创建各种图表和数据可视化,以展示信息和趋势。

在前后端交互上,项目采用前后端分离的写法,前端通过axios根据与后端约定好的api进行交互,发送和得到后端的数据并在客户端渲染,降低了前后端的耦合度。

在项目构建上,项目采用了vite来构建vue3项目。并且通过划分文件夹使得前端项目文件结构清晰,其中

  • api文件夹存储进行前后端交互的api方法的JavaScript文件
  • assets文件夹存储前端需要的静态资源,如css,img和font
  • components文件夹存储可复用的vue组件
  • locates用于国际化配置语言相关文件
  • router文件夹存放vue项目的路由文件
  • store文件夹存放保存的Pinia插件的文件,类似全局变量
  • utils文件夹存放了一些全局的效果配置,如点击鼠标爱心和axios等
  • views文件夹存放基本页面的vue文件

基于上述功能,前端实现了一个Tradereads(北航二手书交易市场)。

参考网址:孔夫子旧书网 (kongfz.com)

1.1.2 实现环境

相关依赖

  • vite: 4.5.0

  • vue: 3.3.4

  • tailwindcss: 3.3.5

  • element-plus: 2.4.3

  • @vue/cli: 5.0.8

  • @element-plus/icons-vue: 2.3.1

  • @vueuse/core: 10.5.0

  • axios: 1.6.0

  • echarts: 5.4.3

  • pinia: 2.1.7

  • postcss: 8.4.31

  • vue-i18n: 9.8.0

  • vue-router: 4.2.5

启动命令:

1
2
3
4
5
# 本地运行
npm run dev
# 云端运行
npm run build
npm run preview

1.2 后端实现环境

后端: Ruby on Rails

Ruby on Rails 负责提供访问数据库的接口

利用 json 格式数据通过 http 协议进行前后端的交互。从前端发来的 http 请求中,根据 Route 将请求分发给不同 Controller 中,由 Controller 来与数据库进行数据存取,最后再通过 render 将包含信息的 json 数据再发送回前端

Ruby on Rails 依赖 Gemfile

1
2
3
4
5
6
7
8
9
10
ruby "3.2.2"
gem "rails", "~> 7.0.8"
gem "sqlite3", "~> 1.4"
gem "puma", "~> 5.0"
gem "jbuilder"
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
gem "bootsnap", require: false
gem "rest-client", "~> 2.1.0"
gem 'ffi', '~> 1.16', '>= 1.16.3'
gem 'rack-cors'

安装依赖

1
bundle install

启动后端指令

1
rails s

二、系统功能结构图

2.1 用户端功能结构图

1

2.2 管理端功能结构图

2

三、基本表的定义,主外码等完整性约束定义,索引的定义

为了实现一个二手书交易网站,主要需要建立以下实体:

Users: table

列名 类型 描述 完整性约束 索引定义
id integer 用户ID 主键约束
phone varchar 手机号码 主键约束
right integer 0: user 1: admin 范围约束 {0,1}

UserDetails: table

列名 类型 描述 完整性约束 索引定义
user_id integer 用户ID 主键约束
外键约束(Users.id )
user.user_detail
指向对应 user_detail 的索引
password varchar 密码
user_name varchar 用户名
buy_address text 购买地址
gender varchar 性别
(男、女)
范围约束 {“male”,“female”}
pay_type text 支付方式
(支付宝、微信、银行卡)
范围约束 {“alipay”,“wechat”,“card”}
avatar varchar 头像图床地址 url

Wallets: table

列名 类型 描述 完整性约束 索引定义
user_id integer 用户 ID 主键约束
外键约束(Users.id )
user.wallet
指向 user 对应 wallet 的索引
money_sum decimal 商品ID 外键约束(Products.id )

Followships: table

列名 类型 描述 完整性约束 索引定义
user_id integer 关注者 主键约束
外键约束(Users.id )
user.followers
用户的被关注列表
following_user_id integer 被关注者 主键约束
外键约束(Users.id )
user.followings
用户的关注列表

Products: table

列名 类型 描述 完整性约束 索引定义
id integer 商品ID 主键约束
user_id integer 用户ID 外键约束(Users.id ) user.products
用户的销售的商品
price decimal 价格
sell_address text 发货地址
store integer 库存
state varchar 销售状态(售完/有货)
check_state integer 0: 未审核 1: 已审核
score_per decimal 评论平均评分

ProductDetails: table

列名 类型 描述 完整性约束 索引定义
product_id integer 商品ID 主键约束
外键约束(Products.id )
product.product_detail
商品的详细信息
product_name text 商品名称
product_image varchar 商品图片
product_press text 商品出版社
product_type varchar 商品类型
(杂志 教科书 小说 童话 戏剧 数学 计算机 漫画 自传)
范围约束
(杂志 教科书 小说 童话 戏剧 数学 计算机 漫画 自传)

Comments: table

列名 类型 描述 完整性约束 索引定义
comment_id integer 评价 ID 主键约束
user_id integer 用户 ID 外键约束(Users.id ) product.comments
商品的评论
product_id integer 商品ID 外键约束(Products.id ) user.comments
用户的评论
content text 评价内容
score integer 评分(从低到高有1,2,3,4,5五个等级) 范围约束(1,2,3,4,5)
created_at datetime(6) 时间戳

Carts: table

列名 类型 描述 完整性约束 索引定义
user_id integer 用户ID 主键约束
外键约束(Users.id )
user.carts
用户的购物车
product_id integer 商品ID 主键约束
外键约束(Products.id )
product.carts
商品所属的购物车
number integer 数量
addTime datetime(6) 时间戳

Orders: table

列名 类型 描述 完整性约束 索引定义
id integer 订单ID 主键约束
user_id integer 购买用户ID 外键约束(Users.id ) user.orders
用户的订单
created_at datetime(6) 订单创建时间戳

OrderItems: table

列名 类型 描述 完整性约束 索引定义
id integer 订单项ID 主键约束
product_id integer 商品ID 外键约束(Products.id ) product.order_items
商品所属的订单项
number integer 数量
state varchar 商品状态(待付款、待发货、待收货、已完成)
order_id integer 订单ID 外键约束(Orders.id ) order.order_items
订单中含有的订单项

Notices: table

列名 类型 描述 完整性约束 索引定义
id integer 公告 ID 主键约束
title varchar 公告标题
notice_type integer 公告类型
1: 系统公告,2: 关注商家上新,3: 支付提醒
范围约束(1,2,3)
user_id integer 公告发布者 ID 外键约束(Users.id ) user.notices
用户发布的公告

NoticeRecords: table

列名 类型 描述 完整性约束 索引定义
id integer 公告已读记录 ID 主键约束
notice_id integer 对应公告 ID 外键约束(Notice.id ) notice.notice_records
公告的已读记录
user_id integer 记录对应的用户 ID 外键约束(User.id ) user.notice_records
用户的已读公告记录

四、系统的安全性设计,不同人员的外模式及相关权限

4.1 系统的安全性设计

  • 对于所有页面,当且仅当登录之后才可以进行后端的交互
  • 管理员管理界面只有管理员身份的人员登录后才是可见状态,管理界面对普通用户不可见

4.2 不同人员的外模式及相关权限

在本应用中,人员分为两类:普通用户和管理员,对于两种人员的相关权限界定如下

普通用户权限

  • 更改个人信息(用户名,密码,购买地址)
  • 管理自己销售的商品
    • 新增商品
    • 更改书籍的库存、价格、发货地址
    • 删除书籍
  • 查看自己的购买订单
  • 管理自己的销售订单
    • 更改订单状态
  • 钱包功能 - 修改支付方式
  • 管理关注列表
    • 查看关注的用户的简要信息
    • 取消关注
    • 查看用户个人主页

管理员权限

  • 所有普通用户拥有的权限

  • 独立发布公告,所有用户可见

  • 管理订单信息

    • 查看所有的订单信息
    • 删除订单信息
  • 审核商品

    • 查看所有商品以及审核状态
    • 修改审核状态(已审核/未审核)

五、存储过程、触发器和函数的代码说明

5.1 整体结构

在本应用中,使用 Ruby On Rails 内置的 Validations 的触发器(检查范围,类型等),机制则是在对象中检查相应字段是否满足条件,若满足条件则将该对象映射存入数据库中

TODO

5.2 触发器函数的代码说明

5.2.1 用户模块

  • 用户的 phone 长度范围为 [3, 20],只能为数字,存在非空约束,唯一约束
  • 用户的权限设置存在非空约束,范围为 {0,1},0 表示普通用户,1 表示管理员
1
2
3
4
5
6
7
8
class User < ApplicationRecord
# phone must be series of number
validates :phone, presence: true, length: { in: 3..20 } ,
numericality: { only_integer: true} ,
uniqueness: true
validates :right, presence: true, inclusion: { in: [0,1] }

end

5.2.2 用户详细信息模块

  • 用户的用户名长度须在 [2, 10] 范围内
  • 用户的密码存在非空约束,长度须在 [6,20] 范围内
  • 用户的性别存在范围约束,须在 {male, female} 集合中
  • 用户的支付方式存在范围约束,须在 {wechat, alipay, bank} 集合中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class UserDetail < ApplicationRecord
validates :user_name, presence: true,
length: { in: 2..10, :message => "Please set your name more than 1 letters and less than 11 letters." }


validates :password, presence: true
validates :password,
length: { in: 6..20, message: "The length of password must >=6 and <=20 ." }

validates :gender, inclusion: { in: %w(male female), message: "%{value} is not available gender." }
validates :pay_type, inclusion: { in: %w(wechat alipay bank),
message: "Pay type %{value} is not supported." }

end

5.2.3 钱包模块

  • 钱包中的钱数必须大于等于 0
1
2
3
class Wallet < ApplicationRecord
validates :money_sum, presence: true, numericality: { :greater_than_or_equal_to => 0, :massage => "Money must be non-negative." }
end

5.2.4 商品模块

  • 商品价格有非空约束,且有范围约束,必须大于等于 0
  • 商品发货地址有非空约束
  • 商品库存有非空约束,且有范围约束,必须为整数且大于等于 0
  • 商品的库存状态有范围约束,Available :有货,StoctOut :缺货
  • 商品的审核状态有范围约束,0 :未审核,1 :审核已通过
1
2
3
4
5
6
7
8
9
10
11
12
13
class Product < ApplicationRecord
validates :price,
:presence => { massage: "Price must exist." }
validates :price, numericality: { :greater_than_or_equal_to => 0, :massage => "Price must be non-negative." }
validates :sell_address,
:presence => { massage: "Selladdress must exist." }
validates :store, presence: true
validates :store, numericality: { only_integer: true, :greater_than_or_equal_to => 0, :massage => "Store must be non-negative." }
validates :state, inclusion: { in: %w(Available StockOut) }

validates :check_state, inclusion: { in: [0, 1] }

end

5.2.5 商品详情模块

  • 商品名字存在非空约束且有范围约束,长度范围在 [1, 20]
  • 商品类型存在范围约束,只能为 {杂志, 教科书, 小说, 童话, 戏剧, 数学, 计算机, 漫画, 自传}
1
2
3
4
5
class ProductDetail < ApplicationRecord
validates :product_name, presence: true, length: { in: 1..20 }
validates :product_type, inclusion: { in: %w(杂志 教科书 小说 童话 戏剧 数学 计算机 漫画 自传) }

end

5.2.6 订单项模块

  • 订单项的数量存在非空约束,且有范围约束,为纯数字且必须大于等于 1
  • 订单项状态存在范围约束,且有范围约束 {“待支付”, “待发货”, “待收货”, “已完成”}
1
2
3
4
5
class OrderItem < ApplicationRecord
validates :number, presence: true, numericality: { greater_than_or_equal_to: 1 }
validates :state, presence: true, inclusion:{ in: ["待支付", "待发货", "待收货", "已完成"] }

end

5.2.7 公告模块

  • 公告存在非空约束,且有范围约束 {1, 2}
1
2
3
class Notice < ApplicationRecord
validates :notice_type, presence: true, inclusion:{ in: [1, 2] }
end

5.2.8 评论模块

  • 评论评分存在非空约束,且有范围约束 {0, 1, 2, 3, 4, 5}
1
2
3
class Comment < ApplicationRecord
validates :score, presence: true, inclusion: { in: [0, 1, 2, 3, 4, 5] }
end

5.3 存储过程以及对应接口

存储过程使用 Ruby On Rails 内置的 Active Record 来进行数据库存取操作

5.3.1 用户模块

注册

请求路径: /api/register

请求方法: POST

请求数据格式:

  • phone :字符串格式,表示用户设置的手机号
  • user_name :字符串格式,表示用户设置的用户名
  • gender :字符串格式,表示用户设置的性别
  • password :字符串格式,表示用户设置的密码

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "This user existed." ——用户已存在
    • "Register failed." ——注册失败(可能是数据不合规范)
    • "Register succeeded." ——注册成功
  • data
    • user_id :当前注册的用户被分配的 id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def register
if User.find_by(phone: params[:phone])
render json: response_json(
false,
message: RegisterError::EXISTED_USER
) and return
end

user = User.new(phone: params[:phone], right: 0)
gender = params[:gender] || "male"
pay_type = "alipay"
user_detail = UserDetail.new(user: user,
password: params[:password],
user_name: params[:user_name],
gender: gender,
pay_type: pay_type)
wallet = Wallet.new(user: user, money_sum: 0.0)
if user.valid? and user_detail.valid? and wallet.valid?
user.save
wallet.save
user_detail.save
session[:current_userid] = user.id
puts "register ------------------------------ #{session[:current_userid]}"
render status: 200, json: response_json(
true,
message: RegisterError::REGISTER_SUCCESS,
data:{
user_id: user.id
}
)
else
render json: response_json(
false,
message: RegisterError::INVALID_INFORMATION
)
end
end

登录

请求路径: /api/login

请求方法: POST

请求数据格式:

  • phone : 字符串格式,表示用户登录使用的手机号
  • password :字符串格式,表示用户登录使用的密码

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Please enter appropriate information." ——输入的手机号或密码为空
    • "Password is wrong." ——密码错误
    • "This user is not existed." ——用户不存在
    • "Login succeeded." ——登录成功
  • data :额外输出的信息
    • user_id :表示用户名在数据库中的 id
    • user_name :字符串格式,表示用户名
    • right :整数格式(0 或 1),0 表示普通用户,1 表示管理员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def login
_phone = params[:phone]
_password = params[:password]
unless _phone || _password
render json: response_json(
false,
message: LoginError::NO_VALID_INFORMATION_PROVIDED,
) and return
end

user = User.find_by(phone: params[:phone])
unless user
render json: response_json(
false,
message: LoginError::USER_NOT_EXISTED
) and return
end

user_detail = user.user_detail
unless user_detail.password == _password
render json: response_json(
false,
message: LoginError::WRONG_PASSWORD_PROVIDED
) and return
end

session[:current_userid] = user.id
puts "--------------#{session[:current_userid]}---------------------"
render status: 200, json: response_json(
true,
message: LoginError::LOGIN_SUCCESS,
data: {
user_id: user.id,
user_name: user_detail.user_name,
right: user.right
}
)
end

登出

请求路径: /api/logout

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
1
2
3
4
5
6
7
def logout
reset_session
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
end

获取用户个人信息

请求路径: /api/users/<user_id>

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Show failed." ——获取信息失败(可能是 id 不存在)
    • "Show succeeded." ——获取成功
  • data :额外输出的信息
    • phone :字符串格式,表示用户电话
    • user_name :字符串格式,表示用户名
    • buy_address :字符串格式,表示用户地址
    • gender :字符串格式,表示用户性别
    • pay_type :字符串格式,表示支付方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def show
@user = User.find(params[:id])
user = @user
unless user
render json: response_json(
false,
message: ShowError::SHOW_FAIL
) and return
end
user_detail = user.user_detail
render status: 200, json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
phone: user.phone,
avatar: user_detail.avatar,
user_name: user_detail.user_name,
buy_address: user_detail.buy_address,
gender: user_detail.gender,
pay_type: user_detail.pay_type,
right: user.right
}
)
end

更改密码

请求路径: /api/users/<user_id>/modify_password

请求方法: POST

请求数据格式:

  • old_password :字符串格式,表示旧密码
  • new_password :字符串格式,表示新密码

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Old password was incorrect." ——旧密码错误
    • "Modify password failed." ——密码更改失败(可能是新密码不合规范)
    • "Modify password succeeded." ——更改成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def modify_password
@user = User.find(params[:user_id])
user = @user
puts "modify_password-------------#{user.id}"
user_detail = user.user_detail
if user_detail.password != params[:old_password]
render json: response_json(
false,
message: ModifyUserError::MODIFY_PASSWORD_WRONG_PASSWORD
)
else
user_detail.password = params[:new_password]
if user_detail.save
render status: 200, json: response_json(
true,
message: ModifyUserError::MODIFY_PASSWORD_SUCCEED
)
else
render json: response_json(
false,
message: ModifyUserError::MODIFY_PASSWORD_FAIL
)
end
end
end

更改用户名

请求路径: /api/users/<user_id>/modify_username

请求方法: POST

请求数据格式:

  • new_username :字符串格式,表示新用户名

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Modify username succeeded." ——更改用户名成功
    • "Modify username failed." ——更改用户名失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def modify_username
@user = User.find(params[:user_id])
user = @user
user_detail = user.user_detail
if params[:new_username]
user_detail.user_name = params[:new_username]
end
if user_detail.save
render status: 200, json: response_json(
true,
message: ModifyUserError::MODIFY_USERNAME_SUCCEED
)
else
render json: response_json(
false,
message: ModifyUserError::MODIFY_USERNAME_FAIL
)
end
end

更改购买地址

请求路径: /api/users/<user_id>/modify_address

请求方法: POST

请求数据格式:

  • new_address :字符串格式,表示新购买地址

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Modify buy address succeeded." ——更改地址成功
    • "Modify buy address failed." ——更改地址失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def modify_address
@user = User.find(params[:user_id])
user = @user
user_detail = user.user_detail
if params[:new_address]
user_detail.buy_address = params[:new_address]
end
if user_detail.save
render status: 200, json: response_json(
true,
message: ModifyUserError::MODIFY_ADDRESS_SUCCEED
)
else
render json: response_json(
false,
message: ModifyUserError::MODIFY_ADDRESS_FAIL
)
end
end

更改支付方式

请求路径: /api/users/<user_id>/modify_pay_type

请求方法: POST

请求数据格式:

  • new_pay_type :字符串格式,表示新支付方式( bankalipaywechat

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded." ——更改地址成功
    • "Failed." ——更改地址失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def modify_pay_type
@user = User.find(params[:user_id])
user = @user
user_detail = user.user_detail
if params[:pay_type]
user_detail.pay_type = params[:pay_type]
end
if user_detail.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

获取所有用户列表

请求路径: /api/users

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
    • "Unauthorized."
  • data :数组格式
    • users :数组格式
      • user_id :用户的id
      • user_phone :字符串格式
      • user_name :用户名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def index
unless is_admin
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
@users = User.all
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
users: @users.collect do |user|
user_detail = user.user_detail
{
user_id: user.id,
user_phone: user.phone,
user_name: user_detail.user_name
}
end
}
)
end

5.3.2 商品模块

查看所有(已审核)商品

请求路径: /api/show_products

请求方法: POST

请求数据格式:

  • show_following :数字格式,1:只显示已关注的用户商品,0:全部显示
  • show_order_base :字符串格式, "price"或"comment"或"score"
  • show_order :字符串格式, "DESC" (降序) 或 "ASC"(升序)

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
  • data :额外输出信息
    • products :数组类型
      • product_id :商品 id
      • product_name :字符串,商品名字
      • product_image :字符串,商品图片地址
      • price :价格
      • product_press :字符串,出版社
      • product_type :字符串,商品类型
      • seller_name :字符串,卖家名字
      • sell_address :字符串,发货地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def show_products
@products = Product.all
show_following = params[:show_following]
show_order_base = params[:show_order_base]
show_order = params[:show_order]
user = current_user

if show_order_base == "price"
if show_order == "DESC" # 降序
products = Product.order(price: :desc)
else # 升序 / 默认
products = Product.order(:price)
end
elsif show_order_base == "comment"
products = Product.all
if show_order == "DESC" # 降序
products = products.sort {|a, b| b.comments.length <=> a.comments.length }
else # 升序 / 默认
products = products.sort {|a, b| a.comments.length <=> b.comments.length }
end
elsif show_order_base == "score"
products = Product.all
if show_order == "DESC" # 降序
products = products.sort {|a, b| b.score_per <=> a.score_per }
else
products = products.sort {|a, b| a.score_per <=> b.score_per }
end
else
products = Product.all
end

products = products.where(check_state: 1)

if show_following == 1
temp = products
products = []
temp.each do |product|
if user.followings.include? product.user
products << product
end
end
end

render json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
products: products.collect do |product|
product_detail = ProductDetail.find_by(product: product)
seller = product.user
{
product_id: product.id,
product_name: product_detail.product_name,
product_image: product_detail.product_image,
price: product.price,
product_press: product_detail.product_press,
product_type: product_detail.product_type,
seller_name: seller.user_detail.user_name,
sell_address: product.sell_address
}
end
}
)
end

添加商品

请求路径: /api/products

请求方法: POST

请求数据格式:

  • price :浮点数,商品价格
  • sell_address :字符串,发货地址
  • store :整型,库存量
  • product_name :字符串,商品名
  • product_image :字符串,商品照片地址
  • product_press :字符串,商品出版社
  • product_type :字符串,书类

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Create product succeeded." ——添加成功
    • "Create product failed." ——添加失败
  • data :额外输出信息
    • product_id :数字,商品 id
    • state :字符串,库存状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def create
seller = current_user

price, sell_address, store = params[:price], params[:sell_address], params[:store]
unless price || sell_address || store
render json: response_json(
false,
message: ProductError::CREATE_FAIL
) and return
end

product = Product.new(user: seller,
price: price,
sell_address: sell_address,
store: store,
state: store == 0 ? "StockOut" : "Available",
check_state: 0,
score_per: 0.0)
name, image, press, type = params[:product_name], params[:product_image], params[:product_press], params[:product_type]
product_detail = ProductDetail.new(product: product,
product_name: name,
product_image: image,
product_press: press,
product_type: type)

notice = Notice.new(title: "新商品上新了", notice_type: 1, user: seller, content: "添加了商品 #{name}")
if product.valid? && product_detail.valid?
product.save
product_detail.save

render status: 200, json: response_json(
true,
message: ProductError::CREATE_SUCCEED,
data: {
product_id: product.id,
state: product.state
}
)
else
render json: response_json(
false,
message: ProductError::CREATE_FAIL
)
end
end

获取商品信息

请求路径: /api/products/<product_id>

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Show product succeeded." ——获取成功
    • "Show product failed." ——获取失败
  • data :额外输出信息
    • product_name :字符串格式,商品名
    • product_image :图片地址
    • product_press :字符串格式,商品出版社
    • product_price :浮点数,商品价格
    • product_stateAvailable 有货,StockOut 缺货
    • seller_name :字符串格式,卖家用户名
    • seller_phone :字符串格式,卖家联系电话
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def show
@product = Product.find(params[:id])
product = @product
unless product
render json: response_json(
false,
message: ProductError::SHOW_PRODUCT_FAIL
) and return
end
product_detail = ProductDetail.find_by(product: product)
seller = User.find_by(id: product.user_id)
render status: 200, json: response_json(
true,
message: ProductError::SHOW_PRODUCT_SUCCEED,
data: {
product_name: product_detail.product_name,
product_image: product_detail.product_image,
product_press: product_detail.product_press,
product_price: product.price.to_f,
product_state: product.state,
seller_name: seller.user_detail.user_name,
seller_phone: seller.phone
}
)
end

获取卖家商品列表

请求路径: /api/users/<user_id>/show_product_list

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Show product list succeeded." ——获取成功
    • "Show product list failed." ——获取失败
  • data :额外输出信息
    • products :数组类型,卖家的商品
      • product_id :数字,商品 id
      • product_name :字符串,商品名
      • product_image :字符串,商品图片地址
      • product_store :数字,商品库存
      • sell_address :字符串,发货地址
      • price :浮点数,商品价格
      • check_state :false 表示商品未审核,true 表示商品审核已通过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def show_product_list
@user = User.find(params[:user_id])
user = @user
unless user
render json: response_json(
false,
message: ProductError::SHOW_PRODUCT_LIST_FAIL
) and return
end
render status: 200, json: response_json(
true,
message: ProductError::SHOW_PRODUCT_LIST_SUCCEED,
data: {
products: user.products.collect do |product|
product_detail = ProductDetail.find_by(product: product)
{
product_id: product.id,
product_name: product_detail.product_name,
product_image: product_detail.product_image,
product_store: product.store,
sell_address: product.sell_address,
price: product.price,
check_state: transfer_state(product.check_state)
}
end
}
)
end

更改库存

请求路径: /api/products/<product_id>/modify_store

请求方法: POST

请求数据格式:

  • new_store :新库存

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Only the seller of the product can modify." ——非卖家不可修改
    • "Modify store failed." ——更改失败(可能是数据不合规格)
    • "Modify store succeeded." ——更改成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def modify_store
@product = Product.find(params[:product_id])
product = @product
unless is_seller
render json: response_json(
false,
message: ProductError::MODIFY_UNAVAILABLE
) and return
end
new_store = params[:new_store]
product.store = new_store
if new_store == 0
product.state = "StockOut"
else
product.state = "Available"
end
if product.save
render status: 200, json: response_json(
true,
message: ProductError::MODIFY_STORE_SUCCEED
)
else
render json: response_json(
false,
message: ProductError::MODIFY_STORE_FAIL
)
end
end

更改价格

请求路径: /api/products/<product_id>/modify_price

请求方法: POST

请求数据格式:

  • new_price :新价格

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Only the seller of the product can modify." ——非卖家不可修改
    • "Modify price failed." ——更改失败(可能是数据不合规格)
    • "Modify price succeeded." ——更改成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def modify_price
@product = Product.find(params[:product_id])
product = @product
unless is_seller
render json: response_json(
false,
message: ProductError::MODIFY_UNAVAILABLE
) and return
end
new_price = params[:new_price]
product.price = new_price
if product.save
render status: 200, json: response_json(
true,
message: ProductError::MODIFY_PRICE_SUCCEED
)
else
render json: response_json(
false,
message: ProductError::MODIFY_PRICE_FAIL
)
end
end

更改商品地址

请求路径: /api/products/<product_id>/modify_sell_address

请求方法: POST

请求数据格式:

  • new_address :新地址

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Only the seller of the product can modify." ——非卖家不可修改
    • "Modify sell address failed." ——更改失败(可能是数据不合规格)
    • "Modify sell address succeeded." ——更改成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def modify_sell_address
@product = Product.find(params[:product_id])
product = @product
puts is_seller
unless is_seller
render json: response_json(
false,
message: ProductError::MODIFY_UNAVAILABLE
) and return
end
new_address = params[:new_address]

product.sell_address = new_address
if product.save
render status: 200, json: response_json(
true,
message: ProductError::MODIFY_ADDRESS_SUCCEED
)
else
render json: response_json(
false,
message: ProductError::MODIFY_ADDRESS_FAIL
)
end
end

将商品添加到购物车

请求路径: /api/products/<product_id>/add_product_to_cart

请求方法: POST

请求数据格式:

  • count :需要添加购买商品的数量

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "There are not enough products in store." ——库存内没有足够的商品
    • "Add fails." ——添加失败
    • "Products have been added to cart." ——添加成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def add_product_to_cart
@product = Product.find(params[:product_id])
product = @product
count = params[:count].to_i
unless count >= 0 && count.is_a?(Integer)
render json: response_json(
false,
message: CartError::ADD_FAIL
) and return
end
if count > product.store
render json: response_json(
false,
message: CartError::NOT_ENOUGH_STORE
) and return
end
product.store -= count
if product.store == 0
product.state = "StockOut"
end
buyer = current_user
cart = Cart.find_by(user: buyer, product: product)
if cart
cart.number += count
else
cart = Cart.new(user: buyer, product: product, number: count)
end

if cart.save
render status: 200, json: response_json(
true,
message: CartError::ADD_SUCCEED
)
else
render json: response_json(
false,
message: CartError::ADD_FAIL
)
end
end

直接购买商品

请求路径: /api/products/<product_id>/buy_product

请求方法: POST

请求数据格式:需要 id

  • count :需要添加购买商品的数量

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "There are not enough products in store." ——库存内没有足够的商品
    • "Failed."
    • "Succeeded."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def buy_product
@product = Product.find(params[:product_id])
product = @product
count = params[:count].to_i
unless count >= 0 && count.is_a?(Integer)
render json: response_json(
false,
message: CartError::ADD_FAIL
) and return
end
if count > product.store
render json: response_json(
false,
message: CartError::NOT_ENOUGH_STORE
) and return
end
product.store -= count
if product.store == 0
product.state = "StockOut"
end

buyer = current_user
order = Order.new(user: buyer)
unless order.valid?
render json: response_json(
false,
message: Global::FAIL
) and return
end
state = "待支付"
order_item = OrderItem.new(product: product, number: count, state: state, order: order)
unless order_item.valid?
render json: response_json(
false,
message: Global::FAIL
) and return
end

order.save
order_item.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)

end

删除商品

请求路径: /api/products/<product_id>

请求方法: DELETE

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Failed."
    • "Succeeded."]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def destroy
@product = Product.find(params[:id])
if @product.user == current_user or current_user.right == 1
if @product.destroy
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

5.3.3 购物车模块

查看当前用户的购物车

请求路径: /api/show_cart

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
  • data :额外输出信息
    • total_price :浮点数形式,购物车内总金额
    • products :数组形式
      • product_id :整数形式,商品 id
      • product_name :字符串形式,商品名字
      • product_image :字符串形式,图片地址
      • seller_name :字符串形式,卖家名字
      • product_price :浮点数,商品单价
      • product_number :整数形式,加入购物车的该商品的数量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def show_cart
user = current_user
total_price = 0
user.carts.each do |cart|
product = cart.product
price = product.price
total_price += price * cart.number
end
render status: 200, json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
total_price: total_price,
products: user.carts.collect do |cart|
product = cart.product
product_detail = ProductDetail.find_by(product: product)
seller = product.user
detail = seller.user_detail
{
product_id: product.id,
product_name: product_detail.product_name,
product_image: product_detail.product_image,
seller_name: detail.user_name,
product_price: product.price,
product_number: cart.number
}
end
}
)
end

选择购物车商品加入订单

请求路径: /api/choose_cart_to_order

请求方法: GET

请求数据格式:

  • choose_carts :数组形式,表示选择的购物车

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Failed."
    • "Succeeded."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def choose_cart_to_order
user = current_user
cart_ids = params[:choose_carts]
carts = []
cart_ids.each do |cart_id|
carts << Cart.find(cart_id)
end
puts "------------------------------#{params}"
puts "-------------------------------#{carts}"
order = Order.new(user: user)
unless order.valid?
render json: response_json(
false,
message: Global::FAIL
) and return
end
order_items = []
carts.carts.each do |cart|
state = "待支付"
order_item = OrderItem.new(product: cart.product, number: cart.number, state: state, order: order)
if order_item.valid?
order_items << order_item
else
render json: response_json(
false,
message: Global::FAIL
) and return
end
end

order.save
order_items.length.times do |i|
carts[i].destroy
order_items[i].save
end
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
end

5.3.4 订单模块

查看当前用户的订单

请求路径: /api/show_current_orders

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
  • data :额外输出信息
    • orders :数组形式
      • order_id :该订单的 id
      • total_price :该订单的总金额
      • order_time :字符串,该订单加入的时间
      • items :数组形式,表示订单项
        • order_item_id :正整数,订单项 id
        • product_image :字符串,商品照片地址
        • product_name :字符串,商品名字
        • sell_address :字符串,发货地址
        • buy_num :正整数,当前用户购买该商品的数量
        • product_price :浮点数,单种商品的总价格(数目*单价)
        • state :字符串,该订单中该商品的状态
          • "toPay" :待支付
          • "toDeliver" :待发货
          • "toTake" :待收货
          • "completed" :已完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def show_current_orders
user = current_user
total_prices = []
puts user.orders.length
user.orders.each do |order|
total_price = 0
order.order_items.each do |item|
product = item.product
price = product.price
total_price += price * item.number
end
total_prices << total_price
end
i = 0
render status: 200, json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
orders: user.orders.collect do |order|
i += 1
{
order_id: order.id,
total_price: total_prices[i-1],
order_time: order.created_at.to_s,
items: order.order_items.collect do |item|
product = item.product
product_detail = ProductDetail.find_by(product: product)
seller = product.user
seller_detail = seller.user_detail
{
order_item_id: item.id,
product_image: product_detail.product_image,
product_name: product_detail.product_name,
sell_address: product.sell_address,
seller_name: seller_detail.user_name,
buy_num: item.number,
product_price: product.price * item.number,
state: item.state
}
end
}
end
}
)
end

查看所有平台订单(admin only)

请求路径: /api/orders

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Unauthorized." :该用户不是管理员
    • "Succeeded."
  • data :额外输出信息
    • orders :数组形式
      • order_id :该订单的 id
      • buyer_id :该订单的买家 id
      • total_price :该订单的总金额
      • order_time :字符串,该订单加入的时间
      • items :数组形式,表示订单项
        • order_item_id :正整数,订单项 id
        • product_image :字符串,商品照片地址
        • product_name :字符串,商品名字
        • sell_address :字符串,发货地址
        • buy_num :正整数,当前用户购买该商品的数量
        • product_price :浮点数,单种商品的总价格(数目*单价)
        • state :字符串,该订单中该商品的状态
          • "toPay" :待支付
          • "toDeliver" :待发货
          • "toTake" :待收货
          • "completed" :已完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def index
user = current_user
if user.right == 0
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
total_prices = []
Order.all.each do |order|
total_price = 0
order.order_items.each do |item|
product = item.product
price = product.price
total_price += price * item.number
end
total_prices << total_price
end
i = 0
render status: 200, json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
orders: Order.all.collect do |order|
i += 1
{
order_id: order.id,
buyer_id: order.user_id,
total_price: total_prices[i-1],
order_time: order.created_at.to_s,
items: order.order_items.collect do |item|
product = item.product
product_detail = ProductDetail.find_by(product: product)
seller = product.user
seller_detail = UserDetail.find_by(user: seller)
{
order_item_id: item.id,
product_image: product_detail.product_image,
product_name: product_detail.product_name,
sell_address: product.sell_address,
seller_name: seller_detail.user_name,
buy_num: item.number,
product_price: product.price * item.number,
state: item.state
}
end
}
end
}
)
end

查看当前用户销售订单项

请求路径: /api/show_sell_orders

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Failed."
    • "Succeeded."
  • data :额外输出信息
    • order_items :数组形式
      • order_id :该订单的 id
      • order_item_id :该订单项 id
      • order_item_number :该订单项的商品数量
      • order_item_state :该订单项的状态
      • product_id :商品 id
      • product_name :商品名
      • product_price :商品单价
      • buyer_id :买家 id
      • order_item_time :订单项创建时间
      • order_item_total_price :订单项总金额
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def show_sell_orders
user = current_user
order_items = []
# sell_orders = OrderItem.select('OrderItem.').joins(:products).where("products.user_id == ?", user.id)
OrderItem.each do |order_item|
product = order_item.product
if product.user == user
order_items << order_item
end
end

puts order_items.length
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
order_items: order_items.collect do |order_item|
order = order_item.order
product = order_item.product
{
order_id: order.id,
order_item_id: order_item.id,
order_item_number: order_item.number,
order_item_state: order_item.state,
product_id: product.id,
product_name: product.product_detail.product_name,
product_price: product.price,
buyer_id: order.user_id,
order_item_time: order_item.created_at.to_s,
order_item_total_price: product.price * order_item.number
}
end
}
)
end

更改订单状态

请求路径: /api/order_items/<order_item_id>/modify_order_item_state

请求方法: POST

请求数据格式:需要 id

  • new_state :新状态("待支付", "待发货", "待收货", "已完成"

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Unauthorized." ——非卖家或管理员不可修改
    • "Failed."
    • "Succeeded."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def modify_state
item = OrderItem.find(params[:order_item_id])
new_state = params[:new_state]
if current_user.right != 1 and current_user != item.order.user
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
item.state = new_state
if item.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

删除订单(admin only)

请求路径: /api/orders/<order_id>

请求方法: DELETE

请求数据格式:无需请求数据,需要 id

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Unauthorized." :非管理员不可操作
    • "Failed."
    • "Succeeded."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def destroy
user = current_user
if user.right == 0
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
if @order.destroy
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

5.3.5 审核模块

获取所有(包括未审核)商品列表

请求路径: /api/products/check_product

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Unauthorized." :非管理员不可操作
    • "Failed."
    • "Succeeded."
  • data :额外输出信息
    • products :数组形式
      • product_id :商品 id
      • product_name :字符串,商品名字
      • product_image :字符串,商品图片地址
      • price :价格
      • product_press :字符串,出版社
      • product_type :字符串,商品类型
      • seller_name :字符串,卖家名字
      • sell_address :字符串,发货地址
      • check_state :0 表示待审核,1 表示审核通过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def get_product_list
user = current_user
if user.right == 0
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
@products = Product.all
products = @products
render json: response_json(
true,
message: Global::SUCCESS,
data: {
products: products.collect do |product|
product_detail = ProductDetail.find_by(product: product)
seller = product.user
{
product_id: product.id,
product_name: product_detail.product_name,
product_image: product_detail.product_image,
price: product.price,
product_press: product_detail.product_press,
product_type: product_detail.product_type,
seller_name: seller.user_detail.user_name,
sell_address: product.sell_address,
check_state: product.check_state
}
end
}
)
end

将某商品的状态设为已审核/已取消

请求路径: /api/products/<product_id>/check_product

请求方法: POST

请求数据格式:需要 id

  • check_result :0 表示审核不通过(商品会删除),1 表示审核通过(商品会加到商品中心)

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Unauthorized." :非管理员不可操作
    • "Failed."
    • "Succeeded."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def check_product
user = current_user
if user.right == 0
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
@product = Product.find(params[:product_id])
product = @product

check_result = params[:check_state]
if check_result == 0
if product.destroy
render status: 200, json: response_json(
true,
message: Global::SUCCESS
) and return
else
render json: response_json(
false,
message: Global::FAIL
) and return
end
end
product.check_state = 1
if product.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

5.3.6 评论模块

给某个商品添加评论

请求路径: /api/products/<product_id>/add_comment

请求方法: POST

请求数据格式:需要 id

  • content :字符串,评论内容
  • score :评价等级,1 到 5

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Failed."
    • "Succeeded."
  • data
    • comment_id :该评论的 id
    • date :字符串形式,该评论的添加日期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def add_comment
@product = Product.find(params[:product_id])
product = @product
content = params[:content]
score = params[:score].to_i
product.score_per = (product.score_per * product.comments.length + score) / (product.comments.length + 1).to_f
comment = Comment.new(product: product, user: current_user, content: content, score: score)
if comment.valid? and product.valid?
comment.save
product.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
comment_id: comment.id,
date: comment.created_at.to_s
}
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

获得某商品的所有评论

请求路径: /api/products/<product_id>/show_comments

请求方法: GET

请求数据格式:需要 id

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Failed."
    • "Succeeded."
  • data :额外输出信息
    • comments :数组形式
      • product_id :所属商品 id
      • user_id :评论人 id
      • content :评论内容
      • score :评分
      • date :评论创建时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def show_comments
product = Product.find(params[:product_id])
comments = product.comments
render json: response_json(
true,
message: Global::SUCCESS,
data: {
comments: comments.collect do |comment|
{
product_id: product.id,
user_id: comment.user_id,
content: comment.content,
score: comment.score,
date: comment.created_at.to_s
}
end
}
)
end

5.3.7 关注模块

关注用户/取关(没关注→关注,关注→取关)

请求路径: /api/users/<user_id>/follow_user

请求方法: GET

请求数据格式:无需请求数据,需要 id

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "You cannot follow yourself." ——不能关注自己
    • "Succeeded."
    • "Failed."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def follow_user
user = User.find(params[:user_id])
if current_user == user
render json: response_json(
false,
message: FollowError::CANNOT_FOLLOW_SELF
) and return
end
if Followship.find(user: current_user, following_user: user)
followship = Followship.find(user: current_user, following_user: user)
if followship.destroy
render status: 200, json: response_json(
false,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end
followship = Followship.new(user: current_user, following_user: user)
if followship.save
render status: 200, json: response_json(
false,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

当前用户是否关注了 user_id

请求路径: /api/users/<user_id>/if_follow

请求方法: GET

请求数据格式:无需请求数据,需要 id

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
  • data :额外输出信息
    • if_followed :true 已关注,false 未关注
1
2
3
4
5
6
7
8
9
def if_follow
user = User.find(params[:user_id])
render status: 200, json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
if_follow: (User.find(user: current_user, following_user: user) != null)
})
end

获取当前用户的关注列表

请求路径: /api/users/<user_id>/follow_list

请求方法: GET

请求数据格式:无需请求数据,需要 id

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
  • data :额外输出信息
    • followings :数组形式
      • following_user_id :已关注的用户 id
      • following_user_name :已关注的用户名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def follow_list
user = User.find(params[:user_id])
render status: 200, json: response_json(
true,
message: ShowError::SHOW_SUCCEED,
data: {
followings: user.followings.collect do |following_user|
following_user_detail = following_user.user_detail
{
following_user_id: following_user.id,
following_user_name: following_user_detail.user_name
}
end
})
end

5.3.8 公告模块

新建公告(admin only)

请求路径: /api/notices

请求方法: POST

请求数据格式:这种公告的 type 为 1

  • title :字符串格式,标题
  • content :字符串格式,内容

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Unauthorized." ——非管理员不可新建公告
    • "Succeeded."
    • "Failed."
  • data :额外返回数据
    • notice_id :该公告的 id
    • notice_create_time :公告创建时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def create
if current_user.right != 1
render json: response_json(
false,
message: Global::UNAUTHORIZED
) and return
end
user = current_user
title = params[:title]
content = params[:content]
notice = Notice.new(title: title, notice_type: 1, user: user, content: content)
if notice.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
notice_id: notice.id,
notice_create_time: notice.created_at.to_s
}
)
else
render status: 200, json: response_json(
true,
message: Global::FAIL
)
end
end

查看当前用户公告

请求路径: /api/notices

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
  • data :额外输出信息
    • notice :数组形式
      • notice_type :1 表示 admin 的公告,2 表示关注的用户的新商品公告
      • notice_user_id :发布公告的用户 id
      • notice_title :公告标题
      • notice_content :公告内容
      • notice_create_time :公告创建时间
      • notice_readed :bool类型,公告是否已读
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def index
@notices = Notice.all
user = current_user
notices = []
@notices.each do |notice|
if user.followings.include? notice.user
notices << notice
elsif notice.user.right == 1
notices << notice
end
end

notices.each do |notice|
unless NoticeRecord.find_by(notice: notice, user: user)
notice_record = NoticeRecord.new(notice: notice, user: user, readed: false)
if notice_record.valid?
notice_record.save
else
puts("-----------notice_record save error!-------------")
end
end
end

render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
notices: notices.collect do |notice|
notice_record = NoticeRecord.find_by(notice: notice, user: user)
{
notice_type: notice.notice_type,
notice_user_id: notice.user_id,
notice_user_name: notice.user.user_detail.user_name,
notice_title: notice.title,
notice_content: notice.content,
notice_create_time: notice.created_at.to_s,
notice_readed: notice_record.readed
}
end
}
)
notices.each do |notice|
notice_record = NoticeRecord.find_by(notice: notice, user: user)
notice_record.readed = true
notice_record.save
end
end

当前用户是否有未读公告

请求路径: /api/have_new_notice

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
  • data :额外输出信息
    • have_new_notice :bool类型,有无未读公告( true 有未读的, false 没有未读的)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def have_new_notice
user = current_user
@notices.each do |notice|
if user.followings.include? notice.user
notice_record = NoticeRecord.find_by(notice: notice, user: user)
if notice_record == null or notice_record.readed == false
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
have_new_notice: true
}
) and return
end
elsif notice.user.right == 1
notice_record = NoticeRecord.find_by(notice: notice, user: user)
if notice_record == null or notice_record.readed == false
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
have_new_notice: true
}
) and return
end
end
end
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
have_new_notice: false
}
)
end

5.3.9 钱包模块

查看当前用户的钱包

请求路径: /api/wallet

请求方法: GET

请求数据格式:无需请求数据

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
  • data :额外输出信息
    • money_sum :实数,已经充值的总金额
1
2
3
4
5
6
7
8
9
10
11
def show_wallet
user = current_user
wallet = user.wallet
render status: 200, json: response_json(
true,
message: Global::SUCCESS,
data: {
money_sum: wallet.money_sum
}
)
end

钱包充值

请求路径: /api/wallet_charge

请求方法: POST

请求数据格式:

  • charge_money :实数,充值金额

返回数据格式:

  • successtruefalse
  • message :错误信息字符串
    • "Succeeded."
    • "Failed."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def wallet_charge
charge_money = params[:charge_money].to_f
user = current_user
wallet = user.wallet
wallet.money_sum += charge_money
if wallet.save
render status: 200, json: response_json(
true,
message: Global::SUCCESS
)
else
render json: response_json(
false,
message: Global::FAIL
)
end
end

六、技术和模块的论述

6.1 数据管理功能

6.1.1 基本数据管理功能

Ruby On Rails 提供了与数据库交互的接口(ActiveRecord),通过对应的数据库操作(如:where, having 等),实现对需要的数据的新增、获取、修改、删除、打印的功能

6.1.2 系统支持 Excel 文件数据的导入

本身数据库可以导入 csv 文件存入数据库中,通过 Rails 中的 ActiveRecord 类通过触发器来剔除不合规的数据,同时数据库本身的完整性约束也在此发挥着作用

6.1.3 数据的获取

通过 python 编写自动化程序在指定网站浏览和抓取数据信息,这样操作可以模拟人的操作行为,自动进行一些重复性的任务,使获取数据和导入数据高效,并且可以实时获取信息。

利用 $requests$ 库通过 HTTP 协议请求网页,并解析网页内容,提取所需的数据。

1
2
resp = requests.get(url, headers=headers)
main_page = BeautifulSoup(resp.text, "html.parser")

使用正则表达获取想要的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
divlist = main_page.find_all("div", class_="item clearfix")
pattern1 = r'<a.*?>(.*?)</a>' # 正则表达
pattern2 = r'<span.*?>(.*?)</span>'
for i in divlist:
name_sub = str(i.find_all("a", class_="link")[0])
s = str(i.find_all("span", class_="text"))

result1 = re.findall(pattern1, name_sub)
result2 = re.findall(pattern2, s)
for x in range(len(result2)):
if '/' in result2[x]:
ss = result2[x].replace('/', '')
result2[x] = ss
print(result2)
name.append(result1[0])
author.append(result2[0])
press.append(result2[1])
time.append(result2[2])
if len(result2) == 4:
price.append('26.00')
else:
price.append(result2[4].replace('¥',''))
num = (int)(random.random() * 31)
province.append(provinces_st[num])

最后导入指定文件

1
2
3
4
5
6
7
8
9
10
11
12
data= {
'name':name,
'author':author,
'press':press,
'time':time,
'price':price,
'province':province

}
df = pd.DataFrame(data)
path = './data.csv'
df.to_csv(path, index=False,encoding='utf_8_sig')

6.2 数据展示功能

6.2.1 基本数据展示功能

前端使用 Vue+vite 将后端获取的关系表中的通过 http 协议接口发送的数据信息进行实时渲染,实现了数据的展示,同时通过后端对数据库实现的升降序数据操作来实现对数据的排序问题,而前端通过筛选的组件来实现多种条件的查询,以及对数据的翻页,跳转等功能,而且采用的是前端预处理的做法,从而避免了每次都要获取信息,降低了时间复杂度的同时不影响对功能的操作。

6.2.2 支持对数据的全文检索

前端可通过模糊搜索功能,对后端传入的视图进行模糊搜索,或者进行非模糊搜索,直接在后端进行检索能够降低时间复杂度,不需要对原数据库进行操作,便能实现这项功能

6.3 业务功能

6.3.1 基本业务功能

本应用的应用场景是二手书交易,实现了商品的购入、卖出、更改、管理,订单的查看、管理、修改、删除,购物车的加入、删除、维护、删除等功能,除此之外还实现了用户之间的关注关系,实现对指定关注用户的查看与管理,并查看对应用户的商品列表

6.3.2 支持对相关人员的业务管理

对于用户新建的商品,并不会立即传送到商品中心,而是转至管理员系统中,由管理员进行审核,只有当商品审核完之后,才能展现在商品中心;除此之外,管理员也可以对网站上所有的订单请求进行查看并更改其状态

6.4 统计分析

能够按照商品类别形成饼状图,按照各类型的数量形成比例

根据每个用户的总销售额进行排名汇总信息,以条形图的方式展现

6.5 安全防护

本应用将用户分为两类:普通用户和管理员,通过注册操作只能够注册普通用户,而管理员是应用本身就已经设置好的。普通用户不能够访问管理员界面,同样也不能够查看网站的全部订单信息、查看所有用户信息。

而对于未登录的用户,其所有功能都不会运行,因为需要有登录后的用户的 id,否则不能实现任何操作

6.6 额外技术及模块

6.6.1 前端鼠标点击特效

通过操作 DOM 元素和使用 requestAnimationFrame 或 setTimeout ,可以在页面全局范围通过javascript实现了一个页面上的爱心效果。通过监听页面点击事件,创建新的爱心元素,并使用动画效果不断更新爱心的位置、大小和透明度,从而实现随机颜色呈现在页面上的动态效果。

6.6.2 前端 404 页面的实现

从安全性角度,本应用实现了 404 页面的渲染,避免了个人中心直接网址访问

6.6.3 上传图片

对于图片的上传和访问,都通过外部 api 访问免费图床实现,对于数据量较小的本应用而言,是足以支撑正常的使用的

image-20231223134734258

七、系统功能的运行实例

7.1 用户功能

7.1.1 登录/注册

提供用户名,密码,手机号等信息即可注册

3

输入电话,密码即可登录,登录成功即可跳转至个人中心界面

4

7.1.2 导航栏

导航栏可根据提示信息跳转至不同界面

image-20231222222013421

7.1.3 个人中心

显示个人信息,并且点击不同按钮可选择不同功能

修改个人信息

修改用户密码

image-20231222222132004

查看我的钱包

点击查看我的钱包,可查看钱包余额9

点击充值即可充值余额

10

可以更改支付方式

11

查看我的购买订单

点击我的购买订单,即可查看相应订单信息

image-20231222222254298

点击垃圾桶图标即可删除相应订单

image-20231222222223099

点击我的销售订单,即可查看相应销售订单信息

点击修改订单状态

15

点击不同维度进行搜索,可模糊匹配

16

查看我的销售商品

点击我的销售商品,即可查看相应销售商品信息

image-20231222222419942

点击编辑商品信息

image-20231222222504208

查看我的关注列表

点击我的关注列表,即可查看相应关注列表信息

19

取消关注

image-20231222222534829

查看主页,主页也可取消关注

image-20231222222557267

退出登录

客服中心——提交反馈给客服

23

7.2 商品中心功能

导航栏点击商品中心

24

根据维度搜索

25

按照维度进行排序26

可选择升序降序

27

立即购买/加入购物车

28

详细信息跳转至商品详情页/查看商家信息跳转商家详情页

29

个人中心页

30

可以发布商品

47

48

可查看类别视图

49

image-20231223125801611

7.3 商品详情页功能

商品详细信息/商品所有评价

image-20231222223100939

发布评论

image-20231222222655091

33

7.4 购物车功能

导航栏点击购物车34

添加商品到订单/看显示商品总价/清空购物车

image-20231222222732387

7.5 公告功能

点击发布公告

image-20231222222838906

image-20231222222858448

点击公共通知

39

我的关注

image-20231222222917389

点击我的销售,可点击查看个人中心

image-20231222223001985

7.6 排行榜功能

导航栏点击排行榜

44

可查看今日推荐(可将推荐内容加入购物车)

可以查看商家排行榜(点击可看主页)

image-20231223125738622

7.7 管理员功能

只有管理能够看到管理员界面导航

image-20231222223256507

订单管理,能够查看平台所有订单详细信息,点击垃圾桶能够删除订单

image-20231222223313513

审核商品,所有商品新建时会送到审核中心供管理员审核,审核通过的商品才能出现在商品中心

image-20231222223429999

用户管理,能够查看平台内所有用户的信息,并且能够访问其个人主页

image-20231222223455667

八、源程序简要说明

对主要数据库相关代码架构已在 五、六 模块进行了详细说明

简要概括为,本系统采用 Ruby on Rails 后端框架。 通过 http 协议与前端通信,根据路由配置将请求分发至不同ActiveController 中来执行不同的数据处理过程, 再由 ActiveController 通过 ActiveRecord 与数据库进行数据存取交互, 最后通过 http 协议将数据返回至前端

九、收获和体会

在开发二手书网站时,采用 Vue 框架作为前端技术栈,而后端则使用 Ruby,这是一个有趣而富有挑战的项目。在这个过程中每个成员的任务分工明确,避免出现任务重叠或者遗漏。可以采用敏捷开发方法,将项目分解成小任务,每个任务有明确的负责人。并且我们通过定期的会议或沟通渠道(每周一次),保持团队的信息流畅通。讨论进展、遇到的问题、解决方案等,有助于及时调整方向和解决潜在的问题。同时,任务和文档采用 notion 共享,代码通过 github 仓库管理,这有助于成员迅速融入项目,也方便日后的维护和升级。

最后大家出色的完成了任务,通过本次数据库大作业,我们不仅对 Vue 框架和 Ruby on Rails 等技术栈有了更深入的理解。学到了如何在项目中有效地应用这些技术,解决技术层面的挑战。更学到了如何更有效地沟通技术细节、解释设计决策、提出问题和解决问题。

这样的团队合作经历不仅提升了技术层面的知识和技能,也培养了团队合作的软技能,使团队更具备应对各种挑战的能力。这些经验将有助于将来的项目开发和更复杂的团队协作。

  • Title: database-archievement-theory
  • Author: Charles
  • Created at : 2023-12-20 08:43:04
  • Updated at : 2024-01-02 15:19:44
  • Link: https://charles2530.github.io/2023/12/20/database-archievement-theory/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
database-archievement-theory