删除收获地址
1 删除收获地址-持久层
1.1 规划sql语句
- 在删除操作之前判断该数据是否存在,判断该条地址的归属是否是当前的用户
- 执行删除收货地址的操作
delete from t_address where aid=?
- 如果用户删除的时默认地址,将剩下地址的某一条作为默认收货地址,自定义规则是:最新修改的收货地址信息是默认地址,可以通过修改时间进行降序排列
limit 0,1 limit(n-1)*n,pageSize,第一个参数时第几页,第二个参数是当前页展示多少条
select * from t_address where uid=? order by modified_time DESC limit 0,1
- 如果用户本身只有一条收货地址,删除后就不用进行其他操作了
1.2 编写接口层以及抽象方法
/**
* 根据aid删除收货地址数据
* @param
* @return
*/
Integer deleteByAid(Integer aid);
/**
* 根据用户id查询用户最后一次修改的收货地址数据
* @param uid 用户id
* @return 返回值是用户最后一次修改的收货地址数据
*/
Address findLastModified(Integer uid);
1.3 xml文件中进行sql映射
<delete id="deleteByAid">
DELETE FROM t_address WHERE aid=#{aid}
</delete>
<!-- 查询该用户最新修改地址的信息 第一页,一条 -->
<select id="findLastModified" resultMap="AddressEntityMap">
select * from t_address where uid=#{uid}
order by modified_time desc limit 0,1
</select>
1.4 进行测试
在AddressMapperTest类中进行测试
@Test
void deleteByAid() {
Integer integer = addressMapper.deleteByAid(7);
System.out.println(integer);
}
@Test
public void findLastModified() {
Address address = addressMapper.findLastModified(9);
System.out.println(address);
}
2 删除收获地址-业务层
2.1 异常规划
在执行删除的时候哦可能会产生未知的删除异常导致数据不能删除成功,则抛出DeleteException异常
package com.cy.store.service.ex;
/**删除数据时产生的异常*/
public class DeleteException extends ServiceException{
public DeleteException() {
super();
}
public DeleteException(String message) {
super(message);
}
public DeleteException(String message, Throwable cause) {
super(message, cause);
}
public DeleteException(Throwable cause) {
super(cause);
}
protected DeleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
2.2 service层接口和抽象方法
/**
* 删除收货地址
* @param aid 收货地址id
* @param uid 用户id
* @param username 用户名
*/
void delete(Integer aid, Integer uid, String username);
2.3 实现类实现接口重写抽象方法
/**
* 删除收货地址
* @param aid 收货地址id
* @param uid 用户id
* @param username 用户名
*/
@Override
public void delete(Integer aid, Integer uid, String username) {
// 判断收货地址是否存在
Address address = addressMapper.findByAid(aid);
if (address==null){
throw new AddressNotFoundException("收货地址数据不存在");
}
// 判断用户id与收货地址数据中的用户id是否相同
if (!address.getUid().equals(uid)){
throw new AccessDeniedException("非法数据访问");
}
// 删除收货地址操作
Integer rows = addressMapper.deleteByAid(aid);
// 如果删除失败,抛出删除异常
if (rows!=1){
throw new DeleteException("删除数据时产生未知的异常");
}
// 统计用户的收货地址数量,如果为0,就直接终止程序
Integer count = addressMapper.countByUid(uid);
if (count==0){
// 直接终止程序
return;
}
// 如果删除的收货地址是默认收货地址,则将用户最新修改的收货地址设置为默认收货地址
if (address.getIsDefault()==1){
// 查询 用户最新修改的收货地址
Address lastModified = addressMapper.findLastModified(uid);
// 设置为默认值
Integer integer = addressMapper.updateDefaultByAid(lastModified.getAid(), username, new Date());
if (integer!=1){
throw new UpdateException("更新数据时产生未知的异常");
}
}
}
2.4 测试业务
在assressServiceTest中进行测试
@Test
void delete() {
addressService.delete(6,9,"admin");
}
3 删除收获地址-控制层
3.1 处理DeleteException异常
在BaseController中需要处理DeleteException
/**
* 控制层基类
*/
public class BaseController {
//操作成功的状态码
public static final int OK = 200;
/**
* 全局异常处理器,用于捕获并处理业务层抛出的异常。
*
* @param e 捕获的异常对象
* @return 返回封装后的 JsonResult 对象,包含状态码和错误信息
*/
// 该项目中产生了异常,会被统一拦截到此方法中,这个方法此时就充当了请求处理方法,方法的返回值直接给前端浏览器,返回给前端的数据中,状态码,状态码对应信息,数据
@ExceptionHandler({ServiceException.class, FileUploadException.class})
public JsonResult<Void> handleException(Throwable e) {
// 创建一个 JsonResult 实例,并初始化异常消息
JsonResult<Void> result = new JsonResult<>(e);
// 判断异常类型,设置对应的状态码和提示信息
if (e instanceof UsernameDuplicatedException){
// 用户名被占用时返回 400 状态码和相应提示
result.setState(4000);
result.setMessage("用户名被占用");
} else if (e instanceof InsertException){
// 插入数据失败时返回 500 状态码和相应提示
result.setState(4000);
result.setMessage("注册时产生未知的异常");
}else if (e instanceof UsernameNotFoundException){
result.setState(4001);
result.setMessage("用户数据不存在");
}else if (e instanceof PasswordNotMatchException){
result.setState(4002);
result.setMessage("用户密码错误");
}else if (e instanceof AddressCountLimitException){
result.setState(4003);
result.setMessage("收货地址超出上限");
}else if (e instanceof AccessDeniedException){
result.setState(4004);
result.setMessage("收货地址数据非法访问");
}else if (e instanceof AddressNotFoundException){
result.setState(4005);
result.setMessage("收货地址数据不存在");
}else if (e instanceof DeleteException){
result.setState(5002);
result.setMessage("删除数据时产生未知的异常");
}else if (e instanceof UpdateException){
result.setState(5003);
result.setMessage("更新数据时产生未知的异常");
} else if (e instanceof FileEmptyException) {
result.setState(6000);
} else if (e instanceof FileSizeException) {
result.setState(6001);
} else if (e instanceof FileTypeException) {
result.setState(6002);
} else if (e instanceof FileStateException) {
result.setState(6003);
} else if (e instanceof FileUploadIOException) {
result.setState(6004);
}
// 返回最终的响应结果
return result;
}
/**
* 从Session中获取当前登录用户的uid
*
* @param session HttpSession对象,用于获取会话中的用户ID
* @return 当前登录用户的uid
*
* `protected` 表示该方法只能被同一个包内的类或子类访问。
* `final` 表示该方法不能被子类重写。
*/
protected final Integer getUidFromSession(HttpSession session) {
// 从Session中获取uid
// 从会话中获取uid属性并转换为整数类型返回
return Integer.valueOf(session.getAttribute("uid").toString());
}
/**
* 从Session中获取当前登录用户的用户名
* @param session HttpSession对象,用于获取会话中的用户名
* @return 当前登录用户的用户名
*/
protected final String getUsernameFromSession(HttpSession session) {
// 从Session中获取当前登录的用户名
return session.getAttribute("username").toString();
}
}
3.2 设计请求
请求路径:/address/delete/{aid}
请求方式:POST
请求参数:Integer aid,HttpSession session
返回值类型:JsonResult<Void>
3.3 编写AddressController类代码,实现请求
/**
* 删除收货地址
* @param aid 收货地址id
* @param session 当前登录的用户的会话
* @return
*/
@RequestMapping("/delete/{aid}")
public JsonResult<Void> delete(@PathVariable("aid") Integer aid, HttpSession session){
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
addressService.delete(aid,uid,username);
return new JsonResult<>(OK);
}
4 Address.html前端页面
4.1 给删除按钮绑定点击事件
4.2 编写deleteById()函数方法,发送请求
function deleteById(aid){
$.ajax({
url: "/addresses/delete/"+ aid,
type: "POST",
dataType: "json",
success: function (json) {
if (json.state == 200) {
alert("删除收货地址成功")
// 将列表的tr清空,替代成新的tr
// 重新调用获取列表的方法
showAddressList();
}else{
alert("删除地址失败")
}
},
error: function (xhr) {
alert("删除收货地址时产生未知的异常"+xhr.message)
}
})
};
但是!!!可能会出现这个问题
Uncaught SyntaxError: Invalid or unexpected token (at address.html:1:12)
在检查代码和业务逻辑没有问题后,发现可能是 #{aid} 是占位符,在后续被替换为真实值。但如果替换失败或未正确处理,最终渲染出来的 HTML 可能会是
<a οnclick="setDefault()" class="btn btn-xs add-def btn-default">设为默认</a>
此时 setDefault() 调用缺少参数,如果函数定义要求必须传参(如 function setDefault(aid)),当点击按钮时就会因为参数缺失导致 JS 报错,甚至出现 Invalid or unexpected token 这类错误
解决方法:
let tr = '<tr>\n' +
'<td>#{tag}</td>\n' +
'<td>#{name}</td>\n' +
'<td>#{address}</td>\n' +
'<td>#{phone}</td>\n' +
'<td><a class="btn btn-xs btn-info"><span class="fa fa-edit"></span> 修改</a></td>\n' +
'<td><a onclick="deleteById(#{aid})" class="btn btn-xs add-del btn-info"><span class="fa fa-trash-o"></span> 删除</a></td>\n' +
'<td><a onclick="setDefault(#{aid})" class="btn btn-xs add-def btn-default">设为默认</a></td>\n' +
'</tr>';
tr = tr.replace("#{tag}", list[i].tag)
.replace("#{name}", list[i].name)
.replace("#{address}", list[i].address)
.replace("#{phone}", list[i].phone)
.replace(/#{aid}/g, list[i].aid); // 注意这里使用全局替换
使用 .replace(/#{aid}/g, ...) 可以确保所有匹配的 #{aid} 都被替换
重启项目运行就没有问题了✌
热销排行
1. 创建数据库表
CREATE TABLE t_product (
id int(20) NOT NULL COMMENT '商品id',
category_id int(20) DEFAULT NULL COMMENT '分类id',
item_type varchar(100) DEFAULT NULL COMMENT '商品系列',
title varchar(100) DEFAULT NULL COMMENT '商品标题',
sell_point varchar(150) DEFAULT NULL COMMENT '商品卖点',
price bigint(20) DEFAULT NULL COMMENT '商品单价',
num int(10) DEFAULT NULL COMMENT '库存数量',
image varchar(500) DEFAULT NULL COMMENT '图片路径',
`status` int(1) DEFAULT '1' COMMENT '商品状态 1:上架 2:下架 3:删除',
priority int(10) DEFAULT NULL COMMENT '显示优先级',
created_time datetime DEFAULT NULL COMMENT '创建时间',
modified_time datetime DEFAULT NULL COMMENT '最后修改时间',
created_user varchar(50) DEFAULT NULL COMMENT '创建人',
modified_user varchar(50) DEFAULT NULL COMMENT '最后修改人',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. 创建实体类
package com.cy.store.entity;
import lombok.Data;
/** 商品数据的实体类 */
@Data
public class Product extends BaseEntity {
private Integer id;
private Integer categoryId;
private String itemType;
private String title;
private String sellPoint;
private Long price;
private Integer num;
private String image;
private Integer status;
private Integer priority;
}
3. 持久层
3.1 编写sql
priority根据优先级进行排序降序
select * from t_product where status=1 order by priority desc limit 0,4
3.2 编写mapper接口以及抽象方法
创建ProductMapper 接口,编写抽象方法,返回值是一个集合
@Mapper
public interface ProductMapper {
/**
* 查询热销商品
* @return 热销商品列表
*/
List<Product> findHotList();
}
3.3 编写sql映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.ProductMapper">
<resultMap id="ProductEntityMap" type="com.cy.store.entity.Product">
<id column="id" property="id"/>
<result column="category_id" property="categoryId"/>
<result column="item_type" property="itemType"/>
<result column="sell_point" property="sellPoint"/>
<result column="created_user" property="createdUser"/>
<result column="created_time" property="createdTime"/>
<result column="modified_user" property="modifiedUser"/>
<result column="modified_time" property="modifiedTime"/>
</resultMap>
<!-- 查询热销商品列表 -->
<!-- 返回值为商品列表,包含id,categoryId,itemType,sellPoint等字段 -->
<!-- SQL说明:从t_product表中查询状态为1(上架)的商品,按优先级降序排列,取前4条记录 -->
<select id="findHotList" resultMap="ProductEntityMap">
select * from t_product where status=1 order by priority desc limit 0,4
</select>
</mapper>
4. 业务层
4.1 service接口编写抽象方法
public interface ProductService {
/** 查询热销商品 */
List<Product> findHotList();
}
4.2 实现类实现接口,重写抽象方法
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
/**
* 查询热销商品列表
* @return 热销商品列表
*/
@Override
public List<Product> findHotList() {
List<Product> list = productMapper.findHotList();
// 将前端用户到的数据设为null,减小体量
for (Product product : list) {
product.setPriority(null);
product.setCreatedUser(null);
product.setCreatedTime(null);
product.setModifiedUser(null);
product.setModifiedTime(null);
}
return list;
}
}
5. controller控制层
5.1 设计请求
请求路径:/products/hotList
请求方式:GET
响应结果:JsonResult<List<Product>>
5.2 将商品的页面进行白名单放行
在LoginInterceptorConfigure类中配置放行白名单
5.3 PruductController类实现请求
package com.cy.store.controller;
import com.cy.store.entity.Product;
import com.cy.store.service.ProductService;
import com.cy.store.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/products")
public class ProductController extends BaseController{
@Autowired
private ProductService productService;
@RequestMapping("/hotList")
public JsonResult<List<Product>> getHotList()
{
List<Product> data = productService.findHotList();
return new JsonResult<>(OK,data);
}
}
6. 前端页面
- 当页面展示时就调用方法获取热门商品列表信息进行渲染
- href="product.html?id=#{id}" 将id拼接到href上,当点击商品信息时,跳转到对应的商品详情页
- /#{image}/g,将所有的属性进行替换
<script type="text/javascript">
$(document).ready(function() {
showHotList();
});
function showHotList() {
$("#hot-list").empty();
$.ajax({
url: "/products/hotList",
type: "GET",
success: function(json) {
var list = json.data;
for (var i = 0; i < list.length; i++) {
console.log(list[i].title);//调试用
var html = '<div class="col-md-12">'
+ '<div class="col-md-7 text-row-2"><a href="product.html?id=#{id}">#{title}</a></div>'
+ '<div class="col-md-2">¥#{price}</div>'
+ '<div class="col-md-3"><img src="..#{image}collect.png" class="img-responsive" /></div>'
+ '</div>';
html = html.replace(/#{id}/g, list[i].id);
html = html.replace(/#{title}/g, list[i].title);
html = html.replace(/#{price}/g, list[i].price);
html = html.replace(/#{image}/g, list[i].image);
$("#hot-list").append(html);
}
}
});
}
</script>