add cart control action

Signed-off-by: Puqns67 <me@puqns67.icu>
This commit is contained in:
Puqns67 2023-09-17 22:08:19 +08:00
parent 3bc0d83714
commit fe3593ef21
Signed by: Puqns67
GPG Key ID: 9669DF042554F536
15 changed files with 209 additions and 19 deletions

View 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"
}
}

View File

@ -0,0 +1,4 @@
package team8.fruitable.controller.action
class Order {
}

View File

@ -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 }
}
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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() {

View File

@ -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?
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -63,4 +63,4 @@
{{>footer}}
</body>
</html>
</html>

View File

@ -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>

View File

@ -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>