add cart control action
Signed-off-by: Puqns67 <me@puqns67.icu>
102
src/main/kotlin/team8/fruitable/controller/action/Cart.kt
Normal file
@ -0,0 +1,102 @@
|
||||
package team8.fruitable.controller.action
|
||||
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.CookieValue
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||
import team8.fruitable.controller.util.error
|
||||
import team8.fruitable.datebase.entity.CartedItem
|
||||
import team8.fruitable.datebase.entity.User
|
||||
import team8.fruitable.datebase.repository.AccountRepository
|
||||
import team8.fruitable.datebase.repository.CartRepository
|
||||
import team8.fruitable.datebase.repository.ItemRepository
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/action/cart")
|
||||
class Cart(
|
||||
private val cartRepository: CartRepository,
|
||||
private val accountRepository: AccountRepository,
|
||||
private val itemRepository: ItemRepository
|
||||
) {
|
||||
private fun getCurrentUser(token: String?): User? {
|
||||
return token?.let(accountRepository::findByToken)
|
||||
}
|
||||
|
||||
@RequestMapping("/add/{id}")
|
||||
fun add(
|
||||
attributes: RedirectAttributes,
|
||||
@CookieValue("TOKEN", required = false) token: String?,
|
||||
@PathVariable("id") id: Long,
|
||||
@RequestParam("redirect", required = false) redirect: String?
|
||||
): String {
|
||||
val user = this.getCurrentUser(token) ?: return error(attributes, "添加商品至购物车", "未登录账户", "/login")
|
||||
val item = itemRepository.findById(id)
|
||||
.orElse(null) ?: return error(attributes, "添加商品至购物车", "商品不存在", "/cart")
|
||||
cartRepository.save(
|
||||
cartRepository.findByUserAndItem(user, item)?.let {
|
||||
it.number += 1; it
|
||||
} ?: CartedItem(user, item))
|
||||
redirect?.let { return "redirect:/${it}" }
|
||||
return "redirect:/items"
|
||||
}
|
||||
|
||||
@RequestMapping("/add/{id}/{number}")
|
||||
fun addByNumber(
|
||||
attributes: RedirectAttributes,
|
||||
@CookieValue("TOKEN", required = false) token: String?,
|
||||
@PathVariable("id") id: Long,
|
||||
@PathVariable("number") number: Int,
|
||||
@RequestParam("redirect", required = false) redirect: String?
|
||||
): String {
|
||||
val user = this.getCurrentUser(token) ?: return error(attributes, "添加商品至购物车", "账户未登录", "/login")
|
||||
val item = itemRepository.findById(id)
|
||||
.orElse(null) ?: return error(attributes, "添加商品至购物车", "商品不存在", "/cart")
|
||||
cartRepository.save(
|
||||
cartRepository.findByUserAndItem(user, item)?.let {
|
||||
it.number += number; it
|
||||
} ?: CartedItem(user, item, number))
|
||||
redirect?.let { return "redirect:/${it}" }
|
||||
return "redirect:/items"
|
||||
}
|
||||
|
||||
@RequestMapping("/remove/{id}")
|
||||
fun remove(
|
||||
attributes: RedirectAttributes,
|
||||
@CookieValue("TOKEN", required = false) token: String?,
|
||||
@PathVariable("id") id: Long,
|
||||
@RequestParam("redirect", required = false) redirect: String?
|
||||
): String {
|
||||
val user = this.getCurrentUser(token) ?: return error(attributes, "将商品从购物车移除", "账户未登录", "/login")
|
||||
val item = itemRepository.findById(id)
|
||||
.orElse(null) ?: return error(attributes, "将商品从购物车移除", "商品不存在", "/cart")
|
||||
cartRepository.findByUserAndItem(user, item)?.let {
|
||||
cartRepository.delete(it)
|
||||
} ?: return error(attributes, "将商品从购物车移除", "在购物车中未找到此商品", "/cart")
|
||||
redirect?.let { return "redirect:/${it}" }
|
||||
return "redirect:/items"
|
||||
}
|
||||
|
||||
@RequestMapping("/remove/{id}/{number}")
|
||||
fun removeByNumber(
|
||||
attributes: RedirectAttributes,
|
||||
@CookieValue("TOKEN", required = false) token: String?,
|
||||
@PathVariable("id") id: Long,
|
||||
@PathVariable("number") number: Int,
|
||||
@RequestParam("redirect", required = false) redirect: String?
|
||||
): String {
|
||||
val user = this.getCurrentUser(token) ?: return error(attributes, "将商品从购物车移除", "账户未登录", "/login")
|
||||
val item = itemRepository.findById(id)
|
||||
.orElse(null) ?: return error(attributes, "将商品从购物车移除", "商品不存在", "/cart")
|
||||
cartRepository.findByUserAndItem(user, item)?.let {
|
||||
it.number -= number
|
||||
if (it.number <= 0)
|
||||
cartRepository.delete(it)
|
||||
else
|
||||
cartRepository.save(it)
|
||||
} ?: return error(attributes, "将商品从购物车移除", "在购物车中未找到此商品", "/cart")
|
||||
redirect?.let { return "redirect:/${it}" }
|
||||
return "redirect:/items"
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package team8.fruitable.controller.action
|
||||
|
||||
class Order {
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package team8.fruitable.datebase.entity
|
||||
|
||||
import jakarta.persistence.*
|
||||
|
||||
@Entity
|
||||
@Table(name = "carts")
|
||||
class CartedItem(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", nullable = false)
|
||||
var id: Long? = null,
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
var user: User? = null,
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "item_id")
|
||||
var item: Item? = null,
|
||||
|
||||
@Column(name = "number", nullable = false)
|
||||
var number: Int = 1
|
||||
) {
|
||||
constructor(user: User, item: Item) : this() {
|
||||
this.user = user
|
||||
this.item = item
|
||||
}
|
||||
|
||||
constructor(user: User, item: Item, number: Int?) : this(user, item) {
|
||||
number?.let { this.number = if (number >= 1) number else 1 }
|
||||
}
|
||||
}
|
@ -33,9 +33,13 @@ class Item(
|
||||
@Column(name = "is_removed", nullable = false)
|
||||
var isRemoved: Boolean? = false,
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "carts")
|
||||
var carted: User? = null,
|
||||
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
name = "carts",
|
||||
joinColumns = [JoinColumn(name = "item_id", nullable = false)],
|
||||
inverseJoinColumns = [JoinColumn(name = "user_id", nullable = false)]
|
||||
)
|
||||
var carted: MutableList<User> = mutableListOf(),
|
||||
|
||||
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
@ -48,13 +52,10 @@ class Item(
|
||||
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
name = "items_to_tags",
|
||||
joinColumns = [JoinColumn(name = "item_id")],
|
||||
inverseJoinColumns = [JoinColumn(name = "tag_id")]
|
||||
joinColumns = [JoinColumn(name = "item_id", nullable = false)],
|
||||
inverseJoinColumns = [JoinColumn(name = "tag_id", nullable = false)]
|
||||
)
|
||||
var tags: MutableList<Tag> = mutableListOf(),
|
||||
|
||||
@OneToOne(mappedBy = "item")
|
||||
var recommend: Recommend? = null,
|
||||
var tags: MutableList<Tag> = mutableListOf()
|
||||
) {
|
||||
constructor(name: String, price: Double) : this() {
|
||||
this.name = name
|
||||
|
@ -12,7 +12,6 @@ class Order(
|
||||
var id: Long? = null,
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "userid")
|
||||
var user: User? = null,
|
||||
|
||||
@Column(name = "time_create", nullable = false)
|
||||
|
@ -11,7 +11,7 @@ class Recommend (
|
||||
var id: Long? = null,
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "item")
|
||||
@JoinColumn(name = "item", nullable = false)
|
||||
var item: Item? = null,
|
||||
|
||||
@Column(name="description", length=32, nullable = false)
|
||||
|
@ -47,10 +47,16 @@ class User(
|
||||
@Column(name = "is_admin", nullable = false)
|
||||
var isAdmin: Boolean = false,
|
||||
|
||||
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "carted")
|
||||
@ManyToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY)
|
||||
@JoinTable(
|
||||
name = "carts",
|
||||
joinColumns = [JoinColumn(name = "user_id", nullable = false)],
|
||||
inverseJoinColumns = [JoinColumn(name = "item_id", nullable = false)]
|
||||
)
|
||||
var carts: MutableList<Item> = mutableListOf(),
|
||||
|
||||
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "user")
|
||||
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id")
|
||||
var orders: MutableList<Order> = mutableListOf()
|
||||
) {
|
||||
constructor(name: String, password: String) : this() {
|
||||
|
@ -0,0 +1,10 @@
|
||||
package team8.fruitable.datebase.repository
|
||||
|
||||
import org.springframework.data.repository.CrudRepository
|
||||
import team8.fruitable.datebase.entity.CartedItem
|
||||
import team8.fruitable.datebase.entity.Item
|
||||
import team8.fruitable.datebase.entity.User
|
||||
|
||||
interface CartRepository : CrudRepository<CartedItem, Long> {
|
||||
fun findByUserAndItem(user: User, item: Item): CartedItem?
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="356.44pt" height="141.39pt" viewBox="0 0 356.44 141.39" version="1.1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="356.44pt" height="141.39pt" viewBox="0 0 356.44 141.39" version="1.1">
|
||||
<defs>
|
||||
<clipPath id="clip1">
|
||||
<path d="M 286 40 L 356.441406 40 L 356.441406 141.390625 L 286 141.390625 Z M 286 40 "/>
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.6 KiB |
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="242.8pt" height="63.6pt" viewBox="0 0 242.8 63.6" version="1.1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="242.8pt" height="63.6pt" viewBox="0 0 242.8 63.6" version="1.1">
|
||||
<defs>
|
||||
<clipPath id="clip1">
|
||||
<path d="M 72 17 L 167 17 L 167 63.601563 L 72 63.601563 Z M 72 17 "/>
|
||||
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.0 KiB |
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="237pt" height="152pt" viewBox="0 0 237 152" version="1.1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="237pt" height="152pt" viewBox="0 0 237 152" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(87.5%,26.699829%,27.799988%);fill-opacity:1;" d="M 166.773438 0.367188 L 143.496094 0.359375 L 166.640625 0.375 C 166.6875 0.375 166.730469 0.367188 166.773438 0.367188 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(92.89856%,10.998535%,14.099121%);fill-opacity:1;" d="M 54.335938 136.753906 L 81.875 14.941406 C 83.6875 6.976563 93.777344 0.515625 101.796875 0.351563 L 48.375 0.335938 C 40.3125 0.335938 29.972656 6.871094 28.128906 14.941406 L 0.59375 136.753906 C 0.425781 137.496094 0.332031 138.222656 0.277344 138.9375 L 0.277344 141.199219 C 0.816406 147.015625 5.261719 151.253906 11.648438 151.351563 L 65.394531 151.351563 C 57.460938 151.230469 52.515625 144.734375 54.335938 136.753906 "/>
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="1122px" height="331px" viewBox="0 0 1122 331" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg width="1122px" height="331px" viewBox="0 0 1122 331" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>编组</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="画板" transform="translate(-183.000000, -354.000000)">
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -63,4 +63,4 @@
|
||||
{{>footer}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>商品 - 在线果蔬商城</title>
|
||||
<title>{{item.name}} - 在线果蔬商城</title>
|
||||
<script type="text/javascript" src="/scripts/header.js"></script>
|
||||
<script type="text/javascript" src="/scripts/clock.js"></script>
|
||||
<script type="text/javascript" src="/scripts/top.js"></script>
|
||||
|
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>商品 - 在线果蔬商城</title>
|
||||
<script type="text/javascript" src="/scripts/header.js"></script>
|
||||
<script type="text/javascript" src="/scripts/clock.js"></script>
|
||||
<script type="text/javascript" src="/scripts/top.js"></script>
|
||||
<script type="text/javascript" src="/scripts/footer.js"></script>
|
||||
<script type="text/javascript" src="/scripts/item.js"></script>
|
||||
<link type="image/x-icon" rel="icon" href="/images/favicon.ico">
|
||||
<!-- 页面公共的样式表 -->
|
||||
<link type="text/css" rel="stylesheet" href="/styles/header.css">
|
||||
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
||||
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
||||
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
||||
<!-- 页面特定的样式表 -->
|
||||
<link type="text/css" rel="stylesheet/less" href="/styles/item.less">
|
||||
<!-- 外部小组件 -->
|
||||
<script src="/scripts/lib/less.min.js"></script>
|
||||
<script src="/scripts/lib/anime.min.js"></script>
|
||||
<script async src="/scripts/lib/explosion.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
{{>header}}
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<div id="Contents">
|
||||
</div>
|
||||
|
||||
{{>footer}}
|
||||
|
||||
</body>
|
||||
</html>
|