栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用SpringBoot编写电脑商城项目笔记(每一步都详细记录,前后端数据交互使用html+ajax+json)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用SpringBoot编写电脑商城项目笔记(每一步都详细记录,前后端数据交互使用html+ajax+json)

项目环境

JDK1.8 Maven3.8.3 Tomcat9.0.54 Mysql8.0

技术栈:springboot+mybatis+mysql+html+javascript+css+json+jquery+ajax+HikariProxy

项目简介:

一款电脑商城项目,主要包括用户、商品、商品类别、收藏、订单、购物车、收货地址等模块功能。项目整体通过json+ajax进行数据传递,实现前端页面的异步数据更新,用户注册账户时密码采用盐值加密存储至数据库,头像上传与显示,还实现了异常获取、拦截器、aop统计业务方法耗时等功能。

完成后的项目地址:https://gitee.com/da-xiong-who-loves-learning/computer-store
如需要原始模板可私信我

框架搭建

    新建一个SpringBoot项目,项目名为store

    选择导入的jar包

    我这边选择lombok、Spring Web、JDBC API、MyBatis framework、MSQL Driver

    测试项目是否能够正常运行(访问http://localhost:8080/)

    创建application.yml,配置数据库的信息

    # 数据库配置
    spring:
      datasource:
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/store?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        driver-class-name: com.mysql.cj.jdbc.Driver
    # mybatis配置
    mybatis:
      type-aliases-package: com.lzj.store.entity
      mapper-locations: classpath:mapper/*.xml
      configuration:
        map-underscore-to-camel-case: true
    

    创建数据库store

    在测试类中测试数据库是否连接成功

    使用默认的Hikari数据源

    @SpringBootTest
    class StoreApplicationTests {
    
        @Autowired
        private DataSource dataSource;//自动装配
    
        @Test
        void getConnection() throws SQLException {
            System.out.println(dataSource.getConnection());
        }
    
    }
    

    测试静态资源是否能够正常访问,将所有的静态资源复制到static目录下

    如果js代码不能够正常去访问可以尝试以下操作

      idea清理缓存clear-installrebuild重新构建重启idea和电脑
功能实现 用户注册 编写数据库
CREATE DATAbase store CHARACTER SET utf8;

CREATE TABLE t_user (
	uid INT AUTO_INCREMENT COMMENT '用户id',
	username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名',
	PASSWORD CHAR(32) NOT NULL COMMENT '密码',
	salt CHAR(36) COMMENT '盐值',
	phone VARCHAR(20) COMMENT '电话号码',
	email VARCHAR(30) COMMENT '电子邮箱',
	gender INT COMMENT '性别:0-女,1-男',
	avatar VARCHAR(50) COMMENT '头像',
	is_delete INT COMMENT '是否删除:0-未删除,1-已删除',
	created_user VARCHAR(20) COMMENT '日志-创建人',
	created_time DATETIME COMMENT '日志-创建时间',
	modified_user VARCHAR(20) COMMENT '日志-最后修改执行人',
	modified_time DATETIME COMMENT '日志-最后修改时间',
	PRIMARY KEY (uid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
编写实体类

    通过表的结构提取除表的公共字段,放在一个实体类的基类中

    //作为实体类的基类
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class baseEntity implements Serializable {
        private String createdUser;//日志-创建人
        private Date createdTime;//日志-创建时间
        private String modifiedUser;//日志-最后修改执行人
        private Date modifiedTime;//日志-最后修改时间
    }
    

    创建用户的实体类。需要继承baseEntity基类

    //用户的实体类
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Component
    public class User extends baseEntity implements Serializable {
        private Integer uid;//用户id
        private String username;//用户名
        private String password;//密码
        private String salt;//盐值
        private String phone;//电话号码
        private String email;//电子邮箱
        private Integer gender;//性别:0-女,1-男
        private String avatar;//头像
        private Integer isDelete;//是否删除:0-未删除,1-已删除
    }
    
注册-持久层

需要在启动类中配置扫描包

@MapperScan("com.lzj.store.mapper")

创建UserMapper接口

public interface UserMapper {
    
    Integer insert(@Param("user") User user);

    
    User findByUsername(@Param("username") String username);
}

在resources目录下创建mapper包,创建UserMapper.xml




    
        insert into t_user (
            username,password,salt,phone,email,gender,avatar,is_delete,
            created_user,created_time,modified_user,modified_time)
        values (#{username},#{password},#{salt},#{phone},#{email},#{gender},#{avatar},#{isDelete},
                #{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
    

    
      
已经有账号?登录

编写ajax完成注册操作

在使用script时,一定要在body末尾编写,尽量不要在head里编写,否则可能出现无法访问的情况

如果放在末尾还出现无法访问的情况,可以参考框架搭建中的静态资源无法访问解决方法


测试

用户登录 登录-持久层

登录页面的持久层可以使用注册页面的语句

    UserMapper

    User findByUsername(String username);
    

    UserMapper.xml

    
          
还没有账号?注册

ajax


测试

session存储用户数据

    在父类baseController中封装两个数据,获取uid和获取username的两个方法,用户头像暂时不考虑,将来封装cookie中来使用

    protected final Integer getuidFromSession(HttpSession session){
        return Integer.valueOf(session.getAttribute("uid").toString());
    }
    
    
    protected final String getUsernameFromSession(HttpSession session){
        return session.getAttribute("username").toString();
    }
    

    在登录的控制器中将数据封装在session对象中,服务本身自动创建有session对象,已经是一个全局的session对象

    @RequestMapping("/login")
    public JsonResult login(String username, String password, HttpSession session){
        User data = userService.login(username, password);
        session.setAttribute("uid",data.getUid());
        session.setAttribute("username",data.getUsername());
        System.out.println(getuidFromSession(session));
        System.out.println(getUsernameFromSession(session));
        return new JsonResult(OK,data);
    
    }
    
拦截器

    在interceptor目录下创建LoginInterceptor类,编写拦截器

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Object uid = request.getSession().getAttribute("uid");
            if(uid==null){
                response.sendRedirect("/web/login.html");
                return false;
            }
            return true;
    
        }
    }
    

    在config目录下注册拦截器,将其配置到spring容器中

    @Configuration//将这个类注册到spring容器中
    public class LoginInterceptorConfigurer implements WebMvcConfigurer {
        //将自定义的拦截器进行注册
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor())
                    .addPathPatterns("
    Integer updatePasswordByUid(@Param("uid") Integer uid,@Param("password") String password,@Param("modifiedUser") String modifiedUser,@Param("modifiedTime") Date modifiedTime);
    
    
    User findByUid(Integer uid);
    

    编写UserMapper.xml

    
        update t_user set
            password=#{password},modified_user=#{modifiedUser},modified_time=#{modifiedTime}
        where uid=#{uid};
    
    
          
       
       

    使用ajax

    
    
个人资料 个人资料-持久层

    规划需要的SQL语句

      更新用户信息的sql语句

      update t_user set phone=?,email=?,gender=?,modified_user=?,modified_time=? where uid=?
      

      根据uid查询用户的数据(可以使用前面功能的语句)

    编写UserMapper

    Integer updateInfoByUid(User user);
    

    编写UserMapper.xml

    
        update t_user set
            phone=#{phone},
            email=#{email},
            gender=#{gender},
            modified_user=#{modifiedUser},
            modified_time=#{modifiedTime}
        where uid=${uid}
    
    

    测试

    @Test
    public void updateInfoByUid(){
        User user = new User();
        user.setUid(10);
        user.setPhone("123456789");
        user.setEmail("123456@qq.com");
        user.setGender(1);
        userMapper.updateInfoByUid(user);
    }
    
个人资料-业务层

    规划异常

      需要将用户信息显示在页面需要判断数据更新时是否产生异常

    编写UserService

    User getByUid(Integer uid);
    
    void changeInfo(Integer uid,String username,User user);
    

    编写UserServiceImpl

    @Override
    public User getByUid(Integer uid) {
        User result = userMapper.findByUid(uid);
        if(result==null||result.getIsDelete()==1){
            throw new UserNotFoundException("用户数据不存在");
        }
    
        User user = new User();
        user.setUsername(result.getUsername());
        user.setPhone(result.getPhone());
        user.setEmail(result.getEmail());
        user.setGender(result.getGender());
    
        return user;
    }
    
    @Override
    public void changeInfo(Integer uid, String username, User user) {
        User result = userMapper.findByUid(uid);
        if(result==null||result.getIsDelete()==1){
            throw new UserNotFoundException("用户数据不存在");
        }
        user.setUid(uid);
        user.setModifiedUser(username);
        user.setModifiedTime(new Date());
        Integer rows = userMapper.updateInfoByUid(user);
        if(rows!=1){
            throw new UpdateException("更新数据时产生未知的异常");
        }
    
    }
    

    测试

    @Test
    public void getByUid(){
        User byUid = userService.getByUid(10);
        System.out.println(byUid);
    }
    @Test
    public void changeInfo(){
        User user = new User();
        user.setPhone("1123456");
        user.setEmail("21221212@qq.com");
        user.setGender(0);
        userService.changeInfo(10,"lzj",user);
    }
    
个人资料-控制层

    处理异常

    编写控制器getByUid

    用于前端页面显示数据

    @RequestMapping("/get_by_uid")
    public JsonResult getByUid(HttpSession session){
        User data = userService.getByUid(getuidFromSession(session));
        return new JsonResult(OK,data);
    }
    

    测试

    编写控制器changeInfo

    @RequestMapping("/change_info")
    public JsonResult changeInfo(User user,HttpSession session){
        //user对象有四部分的数据:username、phone,email,gender
        //uid数据需要再次封装到user对象中
        userService.changeInfo(getuidFromSession(session),getUsernameFromSession(session),user);
    
        return new JsonResult(OK);
    }
    

    测试(http://localhost:8080/users/change_info?phone=1125665867&email=87987654@qq.com&gender=1)

个人资料-前端页面

    原始表单

    ajax编写(两个要求需要实现)

    在打开页面时自动发送ajax请求,查询到的数据填充到这个页面

    检测用户点击了修改按钮后发送一个ajax请求

    
    
上传头像 上传头像-持久层

    规划sql语句

      将对象文件保存在操作系统上,然后把这个文件路径记录在数据库内

      update t_user avatar=?,modifiec_user=?,userdified_time=? where uid=?
      

    编写UserMapper

    Integer updateAvatarByUid(@Param("uid") Integer uid,@Param("avatar") String avatar,@Param("modifiedUser") String modifiedUser,@Param("modifiedTime") Date modifiedTime);
    

    编写UserMapper.xml

    
        update t_user set
            avatar=#{avatar},
            modified_user=#{modifiedUser},
            modified_time=#{modifiedTime}
        where uid=#{uid}
    
    

    编写单元测试

    @Test
    public void updateAvatarByUid(){
        userMapper.updateAvatarByUid(10,"1324654","lzj",new Date());
    }
    
上传头像-业务层

    规划异常

      用户数据不存在更新时产生异常

    编写UserService

    void changeAvatar(Integer uid,String avatar,String username);
    

    编写UserServiceImpl

    @Override
    public void changeAvatar(Integer uid, String avatar, String username) {
        User result = userMapper.findByUid(uid);
        if(result==null||result.getIsDelete()==1){
            throw new UserNotFoundException("用户数据不存在");
        }
        Integer rows = userMapper.updateAvatarByUid(uid, avatar, username, new Date());
        if(rows!=1){
            throw new UpdateException("更新数据时产生未知的异常");
        }
    }
    

    编写单元测试

    @Test
    public void changeAvatar(){
        userService.changeAvatar(10,"qwqreqwr","管理员");
    }
    
上传头像-控制层

    规划异常

    文件异常的父类:

    FileUploadException 泛指文件上传的异常(父类),继承RuntimeException

    父类是:FileUploadException

    FileEmptyExcepetion 文件为空的异常

    FileSizeException 文件大小超出限制

    FileTypeException 文件类型异常

    FileUploadIOException 文件读写的异常

    FileStateException 文件状态异常

    在controller目录下新建ex目录存放异常

    FileUploadException

    public class FileUploadException extends RuntimeException{
        public FileUploadException() {
        }
    
        public FileUploadException(String message) {
            super(message);
        }
    
        public FileUploadException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public FileUploadException(Throwable cause) {
            super(cause);
        }
    
        public FileUploadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    

    FileEmptyExcepetion

    **
     * 

    *文件为空的异常 *

    * * @autor:lzj * @date:2022/3/19 */ public class FileEmptyExcepetion extends FileUploadException{ public FileEmptyExcepetion() { } public FileEmptyExcepetion(String message) { super(message); } public FileEmptyExcepetion(String message, Throwable cause) { super(message, cause); } public FileEmptyExcepetion(Throwable cause) { super(cause); } public FileEmptyExcepetion(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }

    FileSizeException

    **
     * 

    *文件大小超出限制 *

    * * @autor:lzj * @date:2022/3/19 */ public class FileSizeException extends FileUploadException{ public FileSizeException() { } public FileSizeException(String message) { super(message); } public FileSizeException(String message, Throwable cause) { super(message, cause); } public FileSizeException(Throwable cause) { super(cause); } public FileSizeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }

    FileTypeException

    **
     * 

    *文件类型异常 *

    * * @autor:lzj * @date:2022/3/19 */ public class FileTypeException extends FileUploadException{ public FileTypeException() { } public FileTypeException(String message) { super(message); } public FileTypeException(String message, Throwable cause) { super(message, cause); } public FileTypeException(Throwable cause) { super(cause); } public FileTypeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }

    FileUploadIOException

    public class FileUploadIOException extends FileUploadException{
        public FileUploadIOException() {
        }
    
        public FileUploadIOException(String message) {
            super(message);
        }
    
        public FileUploadIOException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public FileUploadIOException(Throwable cause) {
            super(cause);
        }
    
        public FileUploadIOException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    

    FileStateException

    public class FileStateException extends FileUploadException{
        public FileStateException() {
        }
    
        public FileStateException(String message) {
            super(message);
        }
    
        public FileStateException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public FileStateException(Throwable cause) {
            super(cause);
        }
    
        public FileStateException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    

    处理异常,在基类baseController中处理异常

    else if(e instanceof FileEmptyExcepetion){
        result.setState(6000);
        result.setMessage("上传文件产生空异常");
    }else if(e instanceof FileSizeException){
        result.setState(6001);
        result.setMessage("上传文件大小异常");
    }else if(e instanceof FileTypeException){
        result.setState(6002);
        result.setMessage("上传文件类型异常");
    }else if(e instanceof FileStateException){
        result.setState(6003);
        result.setMessage("上传文件状态异常");
    }else if(e instanceof FileUploadIOException){
        result.setState(6004);
        result.setMessage("上传文件产生读写异常");
    }
    

    同时在异常统一处理方法的参数上增加新的异常做处理

    @ExceptionHandler({ServiceException.class,FileUploadException.class})//统一处理抛出的异常
    

    编写控制器

    //设置上传文件的最大值
    public static final int AVATAR_MAX_SIZE=10*1024*1024;
    //限制上传文件的类型
    public static final List AVATAR_TYPE=new ArrayList<>();
    static {
        AVATAR_TYPE.add("image/jpeg");
        AVATAR_TYPE.add("image/png");
        AVATAR_TYPE.add("image/bmp");
        AVATAR_TYPE.add("image/gif");
    }
    
    
    @RequestMapping("/change_avatar")
    public JsonResult changeAvatar(
        HttpSession session,
        @RequestParam("file") MultipartFile file) throws FileNotFoundException {
        //判断文件是否为null
        if(file.isEmpty()){
            throw new FileEmptyExcepetion("文件为空");
        }
        //判断文件大小
        if(file.getSize()>AVATAR_MAX_SIZE){
            throw new FileSizeException("文件超出限制");
        }
        //判断文件类型
        String contentType = file.getContentType();
        //如果集合包含某个元素返回true,这里取反
        if(!AVATAR_TYPE.contains(contentType)){
            throw new FileTypeException("文件类型不支持");
        }
        //上传的文件.../upload/文件.png
        //        String parent=session.getServletContext().getRealPath("/upload");
        //        String parent= ResourceUtils.getURL("classpath:").getPath()+"static/upload/";
        String parent=System.getProperty("user.dir")+"\src\main\resources\static\images\upload";
        System.out.println(parent);
        //File对象指向这个路径,File是否存在
        File dir = new File(parent);
        if(!dir.exists()){//检测目录是否存在
            dir.mkdirs();//创建当前目录
        }
        //获取到这个文件的名称,uuid工具类来生成一个新的字符串作为文件名
        //例如:avatao01.png
        String originalFilename = file.getOriginalFilename();
        int index = originalFilename.lastIndexOf(".");
        String suffix = originalFilename.substring(index);
        String s = UUID.randomUUID().toString().toUpperCase();
        String filename= s+suffix;
    
        //创建一个空文件
        File dest = new File(dir, filename);
        //参数file中数据写入到这个空文件中
        try {
            file.transferTo(dest);//将file文件中的数据写入到dest文件中
        } catch (IOException e) {
            throw new FileUploadIOException("文件读写异常");
        }catch (FileStateException e){
            throw new FileStateException("文件状态异常");
        }
        //返回头像的路径/upload/test.png
        String avatar="/static/images/upload/"+filename;
        System.out.println(avatar);
        userService.changeAvatar(getuidFromSession(session),avatar,getUsernameFromSession(session));
        //返回用户头像的路径给前端页面,将来用于头像展示使用
        return new JsonResult(OK,avatar);
    }
    
    }
    
上传头像-前端页面 使用表单

    如果需要使用表单进行文件的上传,需要给表单显示的添加一个属性enctype="multipart/form-data"声明出来,不会讲目标文件的数据结构做修改再上传


    使用SpringBoot编写电脑商城项目笔记(每一步都详细记录,前后端数据交互使用html+ajax+json)

    测试是否能够上传

使用ajax 显示图片

    原始表单


    使用SpringBoot编写电脑商城项目笔记(每一步都详细记录,前后端数据交互使用html+ajax+json)

    页面中通过ajax请求来提交文件

    
    
登陆后显示头像

使用cookie来存储头像

    前端使用需要导入cookie.js文件(需要放在js引入文件后)

    
    

    在login.html页面的js中调用cookie方法

    //将服务器返回的头像路径设置到cookie中
    $.cookie("avatar",json.data.avatar,{expires: 7});
    

    在uploda.html中引入cookie.js文件(注意,要在放在js引入文件)

    
    

    在upload.html页面通过ready()自动读取cookie中的数据

    $(document).ready(function(){
       let avatar=$.cookie("avatar");
       console.log(avatar);
       //将cookie值获取出来设置到头像的src属性上
       $("#img-avatar").attr("src",avatar);
    });
    

    设置更新完图片后更新cookie

     //将服务器返回的头像路径设置到cookie中
    $.cookie("avatar",json.data,{expires: 7});
    
解决bug 更改默认的大小限制

    SpringMVC默认为1MB文件可以进行上传,手动的去修改SpringMVC上传文件的大小

    方式一:直接在配置文件中进行配置

    spring:
      servlet:
        multipart:
          max-file-size: 10MB
          max-request-size: 15MB
    

    方式二:需要采用ava代码的形式来设置文件的上传大小的限制。主类中进行配置,可以定义一个方法,必须使用@Bean修饰来修饰。在类的前面添加@Configration注解进行修改类。 MutipartConfigElement类型

    @Configuration//表示配置类
    @SpringBootApplication
    @MapperScan("com.lzj.store.mapper")
    public class StoreApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(StoreApplication.class, args);
        }
        @Bean
        public MultipartConfigElement getMultipartConfigElement(){
            //创建一个配置的工厂类对象
            MultipartConfigFactory factory = new MultipartConfigFactory();
            //设置需要创建的对象的相关信息
            factory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
            factory.setMaxRequestSize(DataSize.of(15,DataUnit.MEGABYTES));
    
            //通过工厂类来创建MultipartConfigElement对象
            return factory.createMultipartConfig();
        }
    }
    
新增收货地址 新增收货地址-数据表创建
CREATE TABLE t_address (
	aid INT AUTO_INCREMENT COMMENT '收货地址id',
	uid INT COMMENT '归属的用户id',
	NAME VARCHAR(20) COMMENT '收货人姓名',
	province_name VARCHAR(15) COMMENT '省-名称',
	province_code CHAR(6) COMMENT '省-行政代号',
	city_name VARCHAR(15) COMMENT '市-名称',
	city_code CHAR(6) COMMENT '市-行政代号',
	area_name VARCHAR(15) COMMENT '区-名称',
	area_code CHAR(6) COMMENT '区-行政代号',
	zip CHAR(6) COMMENT '邮政编码',
	address VARCHAR(50) COMMENT '详细地址',
	phone VARCHAR(20) COMMENT '手机',
	tel VARCHAR(20) COMMENT '固话',
	tag VARCHAR(6) COMMENT '标签',
	is_default INT COMMENT '是否默认:0-不默认,1-默认',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (aid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
新增收货地址-实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address extends baseEntity{
    private Integer aid;//收货地址id
    private Integer uid;//归属的用户id
    private String name;//收货人姓名
    private String provinceName;//省-名称
    private String provinceCode;//省-行政代号
    private String cityName;//市-名称
    private String cityCode;//市-行政代号
    private String areaName;//区-名称
    private String areaCode;//区-行政代号
    private String zip;//邮政编码
    private String address;//详细地址
    private String phone;//手机
    private String tel;//固话
    private String tag;//标签
    private Integer isDefault;//是否默认:0-不默认,1-默认
}
新增收货地址-持久层

    规划需要执行的sql语句

      添加收货地址

      insert into t_address (除了aid外字段) values (字段值列表)
      

      一个用户的收货地址规定最多只能有20条数据对应,在插入用户数据之前先做查询

      select count(*) t_address where uid=?
      

    编写AddressMapper

    public interface AddressMapper {
        
        Integer insert(Address address);
    
        
        Integer countByUid(@Param("uid") Integer uid);
    
    }
    

    编写AddressMapper.xml

    
    
    
        
            insert into t_address (uid,name,province_name,province_code,city_name,city_code,area_name,area_code,zip,address,phone,tel,tag,is_default,created_user,created_time,modified_user,modified_time)
                values (#{uid},#{name},#{provinceName},#{provinceCode},#{cityName},#{cityCode},#{areaName},#{areaCode},#{zip},#{address},#{phone},#{tel},#{tag},#{isDefault},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
        
        
          
       
       

    使用ajax实现保存

    
    

    测试

设置默认收货地址 设置默认收货地址-持久层

    规划sql语句

      检查当前用户想设置为默认收货地址的这条数据是否存在

      select * from t_address aid=?
      

      在修改用户的收获默认地址之前,先将所有的收货地址设为非默认

      update t_address set is_default=0 where uid=?
      

      将用户当前选中的这条记录设置为默认收货地址

      update t_address set is_default=1,modified_user=?   where aid=?
      

    编写AddressMapper

    Address findByAid(Integer aid);
    
    
    Integer UpdateNonDefault(Integer uid);
    
    
    
    Integer updateDefaultByAid(@Param("aid") Integer aid,@Param("modifiedUser") String modifiedUser,@Param("modifiedTime") Date modifiedTime);
    

    编写AddressMapper.xml

    
        select * from t_address where uid=#{uid} order by modified_time desc limit 0,1
    
    

    测试

    @Test
    public void deleteByAid(){
        addressMapper.deleteByAid(4);
    }
    @Test
    public void findLastModified(){
        Address lastModified = addressMapper.findLastModified(10);
        System.out.println(lastModified);
    }
    
删除收货地址-业务层

    规划异常

      执行删除时可能导致数据不能成功删除,抛出DeleteException异常

      public class DeleteException extends ServiceException{
          public DeleteException() {
          }
      
          public DeleteException(String message) {
              super(message);
          }
      
          public DeleteException(String message, Throwable cause) {
              super(message, cause);
          }
      
          public DeleteException(Throwable cause) {
              super(cause);
          }
      
          public DeleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
              super(message, cause, enableSuppression, writableStackTrace);
          }
      }
      

    编写AddressService

    void delete(Integer aid,Integer uid,String username);
    

    编写AddressServiceImpl

    @Override
    public void delete(Integer aid, Integer uid, String username) {
        Address result = addressMapper.findByAid(aid);
        if(result==null){
            throw new AddressNotFountException("收货地址不存在");
        }
        //判断当前获取到的收货地址数据的归属
        if(!uid.equals(result.getUid())){
            throw new AccessDeniedException("非法数据访问");
        }
        Integer rows = addressMapper.deleteByAid(aid);
        if(rows!=1){
            throw new DeleteException("删除数据产生异常");
        }
        Integer count = addressMapper.countByUid(uid);
        if(count==0){
            return;
        }
        Address address = addressMapper.findLastModified(uid);
        if(result.getIsDefault()==1){
            Integer row = addressMapper.updateDefaultByAid(address.getAid(), username, new Date());
            if(row!=1){
                throw new UpdateException("更新数据时产生未知异常");
            }
        }
    }
    

    测试

    @Test
    public void delete(){
        addressService.delete(5,10,"管理员");
    }
    
删除收货地址-控制层

    处理异常,baseController中

    else if(e instanceof DeleteException){
        result.setState(5002);
        result.setMessage("删除数据时产生未知的异常");
    }
    

    编写控制器

    @RequestMapping("/{aid}/delete")
    public JsonResult delet(@PathVariable("aid") Integer aid,HttpSession session){
        addressService.delete(aid,getuidFromSession(session),getUsernameFromSession(session));
        return new JsonResult<>(OK);
    }
    

    测试

删除收货地址-前端页面

    添加删除按钮的事件

     删除
    

    编写相关函数

    function deleteByAid(aid){
       $.ajax({
          url:"/addresses/"+aid+"/delete",
          type:"POST",
          dataType:"JSON",
          success: function (json){
             if (json.state == 200){
                //清空列表内容
                $("#address-list").empty();
                //重新展示列表
                showAddressList();
                // location.href="./address.html";
             }else{
                alert("删除收货地址失败");
             }
          },
          error: function (xhr){
             alert("删除收货地址时产生未知的异常"+xhr.message);
          }
       });
    }
    

    测试

商品热销排行 商品-创建数据库
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;
商品-实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product extends baseEntity implements Serializable {
    private Integer id;//商品id
    private Integer categoryId;//分类id
    private String itemType;//商品系列
    private String title;//商品标题
    private String sellPoint;//商品卖点
    private Long price;//商品单价
    private Integer num;//库存数量
    private String image;//图片路径
    private Integer status;//商品状态  1:上架   2:下架   3:删除
    private Integer priority;//显示优先级
}
商品-持久层

    规划sql语句

      查询热销商品列表的sql语句

      select * from t_product where status=1 order by priority desc limit 0,4
      

    编写ProduceMapper

    public interface ProductMapper {
        ListfindHotList();
    }
    

    编写ProduceMapper.xml

    
    
    
        
            select * from t_product where status=1 order by priority desc limit 0,4
        
    
    

    单元测试

    //@SpringBootTest:标注当前的类是一个测试类,不会随同项目一块打包
    @SpringBootTest
    //@RunWith:表示启动这个单元测试类(单元测试类是不能够运行的),需要传递一个参数,必须是SpringRunner实例类型
    @RunWith(SpringRunner.class)
    public class ProductMapperTests {
        @Autowired
        ProductMapper productMapper;
        @Test
        public void findHotList(){
            List hotList = productMapper.findHotList();
            for (Product product : hotList) {
                System.out.println(product);
            }
        }
    }
    
商品-业务层

    编写ProductService

    public interface ProductService {
        List findHotList();
    }
    

    编写ProductServiceImpl

    @Service
    public class ProductServiceImpl implements ProductService {
        @Autowired
        private ProductMapper productMapper;
        @Override
        public List findHotList() {
            List hotList = productMapper.findHotList();
            for (Product product : hotList) {
                product.setPriority(null);
                product.setCreatedUser(null);
                product.setCreatedTime(null);
                product.setModifiedUser(null);
                product.setModifiedTime(null);
            }
            return hotList;
        }
    }
    
商品-控制层

    编写控制器

    @RestController
    @RequestMapping("/products")
    public class ProductController extends baseController {
        @Autowired
        private ProductService productService;
    
        @RequestMapping("/host_list")
        public JsonResult> findHotList(){
            List data = productService.findHotList();
            return new JsonResult<>(OK,data);
        }
    }
    

    将路径加入到拦截器中放行

    "/products
        Integer insert(Cart cart);
    
        
        Integer updateNumByCid(Integer cid, Integer num, String modifiedUser, Date modifiedTime);
    
        
        Cart findByUidAndPid(Integer uid,Integer pid);
    }
    

    编写CartMapper.xml

    
    
    
        
            insert into t_cart (uid,pid,price,num,created_user,created_time,modified_user,modified_time)
            values (#{uid},#{pid},#{price},#{num},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
        
        
            update t_cart set
                num=#{num},
                modified_user=#{modifiedUser},
                modified_time=#{modifiedTime}
            where cid=#{cid}
        
        
        select cid,uid,pid,t_cart.price,t_cart.num,t_product.title,t_product.image,t_product.price as realprice
        from t_cart left join t_product on t_cart.pid=t_product.id
        where uid=#{uid}
        order by t_cart.created_time desc
    
    

    单元测试

    @Test
    public void findVOByUid(){
        List voByUid = cartMapper.findVOByUid(10);
        for (CartVo cartVo : voByUid) {
            System.out.println(cartVo);
        }
    }
    
显示购物车列表-业务层

    规划异常,查询不需要规划

    编写CartService

    List getVOByUid(Integer uid);
    

    编写CartServiceImpl

    @Override
    public List getVOByUid(Integer uid) {
        return cartMapper.findVOByUid(uid);
    }
    
显示购物车列表-控制层

    处理异常,无需要新增的异常

    编写控制器

    @RequestMapping({"","/"})
    public JsonResult> getVOByUid(HttpSession session){
        List data = cartService.getVOByUid(getuidFromSession(session));
        return new JsonResult<>(OK,data);
    }
    

    测试http://localhost:8080/carts

显示购物车列表-前端页面

cart.html

    注释掉cart.js文件

      

    修改结算按钮,修改为type=“button”

    
    

    找到tbody标签

    
       
          
             
          
          
    使用SpringBoot编写电脑商城项目笔记(每一步都详细记录,前后端数据交互使用html+ajax+json)
    联想(Lenovo)小新Air13 Pro 13.3英寸14.8mm超轻薄笔记本电脑 ¥5298

    编写ajax和函数,

    
    

    测试

增加购物车商品数量 增加购物车商品数量-持久层

    规划sql语句

      执行更新t_cart表记录的num的值,无需重复开发

      根据cid的值来查询当前的购物车这条数据是否存在

      select * from t_cart where cid=#{cid}
      

    编写CartMapper

    Cart findByCid(Integer cid);
    

    编写CartMapper.xml

    
        select * from t_cart where cid=#{cid}
    
    

    单元测试

    @Test
    public void findByCid(){
        Cart byCid = cartMapper.findByCid(2);
        System.out.println(byCid);
    }
    
增加购物车商品数量-业务层

    规划异常,之前已经写过

      更新时可能产生异常

      查询的数据是否有访问的权限

      要查询的数据不存在,抛出CartNotFoundException

      public class CartNotFoundException extends ServiceException{
          public CartNotFoundException() {
          }
      
          public CartNotFoundException(String message) {
              super(message);
          }
      
          public CartNotFoundException(String message, Throwable cause) {
              super(message, cause);
          }
      
          public CartNotFoundException(Throwable cause) {
              super(cause);
          }
      
          public CartNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
              super(message, cause, enableSuppression, writableStackTrace);
          }
      }
      

    编写CartService

    Integer addNum(Integer cid,Integer uid,String username);
    

    编写CartServiceImpl

    @Override
    public Integer addNum(Integer cid, Integer uid, String username) {
        Cart result = cartMapper.findByCid(cid);
        if(result == null){
            throw new CartNotFoundException("数据不存在");
        }
        if(!result.getUid().equals(uid)){
            throw new AccessDeniedException("数据非法访问");
        }
        Integer integer = cartMapper.updateNumByCid(cid, result.getNum() + 1, username, new Date());
        if (integer!=1){
            throw new UpdateException("更新数据失败");
        }
        return result.getNum() + 1;
    }
    

    单元测试

    @Test
    public void addNum(){
        cartService.addNum(3,10,"管理员");
    }
    
增加购物车商品数量-控制层

    处理异常

    else if(e instanceof CartNotFoundException){
        result.setState(4007);
        result.setMessage("购物车数据不存在的异常");
    }
    

    编写控制器

    @RequestMapping("/{cid}/num/add")
    public JsonResult addNum(HttpSession session,Integer cid){
        Integer data = cartService.addNum(cid, getuidFromSession(session), getUsernameFromSession(session));
        return new JsonResult<>(OK,data);
    }
    

    测试http://localhost:8080/carts/3/num/add

增加购物车商品数量-前端页面

    修改按钮的点击事件

    
    

    编写函数和ajax

    function addNum(cid){
       $.ajax({
          url: "/carts/"+cid+"/num/add",
          type: "POST",
          dataType: "JSON",
          success: function (json){
             if (json.state==200){
                $("#goodsCount"+cid).val(json.data);
                //获取某个标签内部的内容:文本或标签
                let price = $("#goodsPrice"+cid).html();
                let totalPrice = price * json.data;
                $("#goodsCast"+cid).html(totalPrice);
             }else{
                alert("增加购物车商品数量失败")
             }
          },
          error: function (xhr){
             alert("添加购物车商品数量产生未知的异常,"+xhr.message)
          }
       });
    }
    

    测试

显示勾选的购物车数据 显示勾选的购物车数据-持久层

    规划sql语句

      点击结算按钮后展示用户在上个页面所勾选的购物车对应的数据,需要讲用户勾选的多个cid传给下个页面

      select cid,uid,pid,t_cart.price,t_cart.num,t_product.title,t_product.image,t_product.price as realprice
      from t_cart left join t_product on t_cart.pid=t_product.id
      where cid in (?,?,?)
      order by t_cart.createdTime desc
      

    编写CartMapper

    List findVOByCid(@Param("cids") List cids);
    

    编写CartMapper.xml

    
        select cid,uid,pid,t_cart.price,t_cart.num,t_product.title,t_product.image,t_product.price as realprice
        from t_cart left join t_product on t_cart.pid=t_product.id
        where cid in
        
            #{cid}
        
        order by t_cart.created_time desc
    
    

    单元测试

    @Test
    public void findVOByCid(){
        ArrayList integers = new ArrayList<>();
        integers.add(1);
        integers.add(2);
        integers.add(3);
        System.out.println(cartMapper.findVOByCid(integers));
    }
    
显示勾选的购物车数据-业务层

    规划异常,没有需要进行规划的异常

    编写CartService

    List getVOByCid(List cids,Integer uid);
    

    编写CartServiceImpl

    @Override
    public List getVOByCid(List cids, Integer uid) {
        List list = cartMapper.findVOByCid(cids);
        Iterator it = list.iterator();
        while (it.hasNext()){
            CartVo cartVo = it.next();
            if(!cartVo.getUid().equals(uid)){
                //从集合中移除这个元素
                //必须使用迭代器的remove
                it.remove();
            }
        }
        return list;
    }
    

    单元测试

    @Test
    public void getVOByCid(){
        ArrayList integers = new ArrayList<>();
        integers.add(1);
        integers.add(2);
        integers.add(3);
        System.out.println(cartService.getVOByCid(integers,10));
    }
    
显示勾选的购物车数据-控制层

    处理异常,无需要处理的异常

    编写控制器

    @RequestMapping("/list")
    //默认是数组形式传递,如果需要传递list,则需要使用@RequestParam注解
    public JsonResult> getVOByCid(@RequestParam List cids, HttpSession session){
        List data = cartService.getVOByCid(cids,getuidFromSession(session));
        return new JsonResult<>(OK,data);
    }
    

    测试http://localhost:8080/carts/list?cids=1&cids=2&cids=3

显示勾选的购物车数据-前端页面

order/confirm/i.html

    将cart.html页面中的结算按钮修改属性

    
    

    修改跳转表单位get请求

    注释order/confirm/i.html中引入的js文件

    
    

    编写函数和ajax语句

    
    
确定订单页显示收货地址 前端页面

order/confirm/i.html

    收货地址存放在一个select下拉列表中,将查询到的当前登录用户的收货地址动态的加载到这个下拉列表中。之前已经开发过通过uid查询收货地址的数据

    order/confirm/i.html页面中,收货地址数据的展示需要自动进行加载,编写相关函数

    $(document).ready(function(){
       showCartList();
       showAddressList();
    });
    

    编写函数和ajax

    function showAddressList(){
       //清空tbody标签中的数据
       $("#address-list").empty();
       $.ajax({
          url: "/addresses/",
          type: "get",
          dataType: "JSON",
          success: function (json){
             if (json.state==200){
                let list=json.data;
                for (let i = 0; i < list.length ;i++) {
                   let opt='';
                   opt=opt.replace(/#{aid}/g,list[i].aid);
                   opt=opt.replace(/#{name}/g,list[i].name);
                   opt=opt.replace(/#{tag}/g,list[i].tag);
                   opt=opt.replace(/#{provinceName}/g,list[i].provinceName);
                   opt=opt.replace(/#{cityName}/g,list[i].cityName);
                   opt=opt.replace(/#{areaName}/g,list[i].areaName);
                   opt=opt.replace(/#{address}/g,list[i].address);
                   opt=opt.replace(/#{phone}/g,list[i].phone);
    
                   $("#address-list").append(opt);
                }
             }
          },
          error: function (xhr){
             alert("购物车收货地址列表数据加载产生未知的异常,"+xhr.message)
          }
       });
    }
    
创建订单 创建订单-数据库
CREATE TABLE t_order (
	oid INT AUTO_INCREMENT COMMENT '订单id',
	uid INT NOT NULL COMMENT '用户id',
	recv_name VARCHAR(20) NOT NULL COMMENT '收货人姓名',
	recv_phone VARCHAR(20) COMMENT '收货人电话',
	recv_province VARCHAR(15) COMMENT '收货人所在省',
	recv_city VARCHAR(15) COMMENT '收货人所在市',
	recv_area VARCHAR(15) COMMENT '收货人所在区',
	recv_address VARCHAR(50) COMMENT '收货详细地址',
	total_price BIGINT COMMENT '总价',
	STATUS INT COMMENT '状态:0-未支付,1-已支付,2-已取消,3-已关闭,4-已完成',
	order_time DATETIME COMMENT '下单时间',
	pay_time DATETIME COMMENT '支付时间',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (oid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

CREATE TABLE t_order_item (
	id INT AUTO_INCREMENT COMMENT '订单中的商品记录的id',
	oid INT NOT NULL COMMENT '所归属的订单的id',
	pid INT NOT NULL COMMENT '商品的id',
	title VARCHAR(100) NOT NULL COMMENT '商品标题',
	image VARCHAR(500) COMMENT '商品图片',
	price BIGINT COMMENT '商品价格',
	num INT COMMENT '购买数量',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (id)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
创建订单-实体类

    订单实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Order extends baseEntity{
        private Integer oid;//订单id
        private Integer uid;//用户id
        private String recvName;//收货人姓名
        private String recvPhone;//收货人电话
        private String recvProvince;//收货人所在省
        private String recvCity;//收货人所在市
        private String recvArea;//收货人所在区
        private String recvAddress;//收货详细地址
        private Long totalPrice;//总价
        private Integer status;//状态:0-未支付,1-已支付,2-已取消,3-已关闭,4-已完成
        private Date orderTime;//下单时间
        private Date payTime;//支付时间
    }
    

    订单项的实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class OrderItem extends baseEntity{
        private Integer id;//订单中的商品记录的id
        private Integer oid;//所归属的订单的id
        private Integer pid;//商品的id
        private String title;//商品标题
        private String image;//商品图片
        private Long price;//商品价格
        private Integer num;//购买数量
    }
    
创建订单-持久层

    规划sql语句

      将数据插入到订单表中

      insert into t_order (oid外所有的字段) values (字段的值)
      

      将数据插入到订单项的表中

      insert into t_order_item (id外所有的字段) values (字段的值)
      

    编写OrderMapper

    public interface OrderMapper {
        
        Integer insertOrder(Order order);
    
        
        Integer insertOrderItem(OrderItem orderItem);
    }
    

    编写OrderMapper.xml

    
    
    
        
            insert into t_order (uid,recv_name,recv_phone,recv_province,recv_city,recv_area,recv_address,total_price,status,order_time,pay_time,created_user,created_time,modified_user,modified_time)
            values (#{uid},#{recvName},#{recvPhone},#{recvProvince},#{recvCity},#{recvArea},#{recvAddress},#{totalPrice},#{status},#{orderTime},#{payTime},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
    
        
        
            insert into t_order_item (oid,pid,title,image,price,num,created_user,created_time,modified_user,modified_time)
            values (#{oid},#{pid},#{title},#{image},#{price},#{num},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
        
    
    
    

    单元测试

    //@SpringBootTest:标注当前的类是一个测试类,不会随同项目一块打包
    @SpringBootTest
    //@RunWith:表示启动这个单元测试类(单元测试类是不能够运行的),需要传递一个参数,必须是SpringRunner实例类型
    @RunWith(SpringRunner.class)
    public class OrderMapperTests {
        @Autowired
        OrderMapper orderMapper;
        @Test
        public void insertOrder(){
            Order order = new Order();
            order.setUid(10);
            order.setRecvName("红红");
            order.setRecvPhone("111556423");
            orderMapper.insertOrder(order);
        }
        @Test
        public void insertOrderItem(){
            OrderItem orderItem = new OrderItem();
            orderItem.setOid(1);
            orderItem.setPid(10000003);
            orderItem.setTitle("广博(GuangBo)16K115页线圈记事本子日记本文具笔记本图案随机");
            orderMapper.insertOrderItem(orderItem);
    
        }
    }
    
创建订单-业务层

    规划异常

      在AddressService接口中定义根据收货地址的id获取收货地址数据

      Address getByAid(Integer aid,Integer uid);
      

      编写实现类

      @Override
      public Address getByAid(Integer aid,Integer uid) {
          Address address = addressMapper.findByAid(aid);
          if(address==null){
              throw new AddressNotFountException("收货地址数据不存在");
          }
          if(!address.getUid().equals(uid)){
              throw new AccessDeniedException("发发数据访问");
          }
          address.setProvinceCode(null);
          address.setCityCode(null);
          address.setAreaCode(null);
          address.setModifiedUser(null);
          address.setCreatedUser(null);
          address.setModifiedTime(null);
          address.setCreatedTime(null);
          return address;
      }
      

    编写OrderService

    public interface OrderService {
        Order create(Integer aid, Integer uid, String username, List cids);
    
    }
    

    编写OrderServiceImpl

    @Service
    public class OrderServiceImpl implements OrderService {
        @Autowired
        private OrderMapper orderMapper;
        @Autowired
        private AddressService addressService;
        @Autowired
        private CartService cartService;
    
        @Override
        public Order create(Integer aid, Integer uid, String username, List cids) {
            List list = cartService.getVOByCid(cids, uid);
            //计算产品的总价
            Long totalPrice=0L;
            for (CartVo cartVo : list) {
                totalPrice+=cartVo.getRealPrice()*cartVo.getNum();
            }
    
            Address address = addressService.getByAid(aid, uid);
            Order order = new Order();
            order.setUid(uid);
            order.setRecvName(address.getName());
            order.setRecvPhone(address.getPhone());
            order.setRecvProvince(address.getProvinceName());
            order.setRecvCity(address.getCityName());
            order.setRecvArea(address.getAreaName());
            order.setRecvAddress(address.getAddress());
            //状态、总价、时间
            order.setStatus(0);
            order.setTotalPrice(totalPrice);
            order.setOrderTime(new Date());
            //日志
            order.setCreatedUser(username);
            order.setModifiedUser(username);
            order.setCreatedTime(new Date());
            order.setModifiedTime(new Date());
    
            Integer integer = orderMapper.insertOrder(order);
            if(integer!=1){
                throw new InsertException("插入数据异常");
            }
    
            //创建订单详细项的数据
    
            for (CartVo cartVo : list) {
                //创建一个订单项数据对象
                OrderItem orderItem = new OrderItem();
                orderItem.setOid(order.getOid());
                orderItem.setPid(cartVo.getPid());
                orderItem.setTitle(cartVo.getTitle());
                orderItem.setImage(cartVo.getImage());
                orderItem.setPrice(cartVo.getRealPrice());
                orderItem.setNum(cartVo.getNum());
                //日志
                orderItem.setCreatedUser(username);
                orderItem.setModifiedUser(username);
                orderItem.setCreatedTime(new Date());
                orderItem.setModifiedTime(new Date());
                //插入数据
                Integer integer1 = orderMapper.insertOrderItem(orderItem);
                if(integer1!=1){
                    throw new InsertException("插入数据异常");
                }
            }
    
            return order;
        }
    }
    

    单元测试

    //@SpringBootTest:标注当前的类是一个测试类,不会随同项目一块打包
    @SpringBootTest
    //@RunWith:表示启动这个单元测试类(单元测试类是不能够运行的),需要传递一个参数,必须是SpringRunner实例类型
    @RunWith(SpringRunner.class)
    public class OrderServiceTests {
        @Autowired
        OrderService orderService;
    
        @Test
        public void create(){
            ArrayList list = new ArrayList<>();
            list.add(3);
            list.add(4);
            Order order = orderService.create(10, 10, "红红", list);
            System.out.println(order);
        }
    
    }
    
创建订单-控制层

    处理异常,没有需要处理的异常

    编写控制器

    @RestController
    @RequestMapping("/orders")
    public class OrderController extends baseController{
        @Autowired
        private OrderService orderService;
        @RequestMapping("/create")
        public JsonResult create(Integer aid, @RequestParam List cids, HttpSession session){
            Order data = orderService.create(aid, getuidFromSession(session), getUsernameFromSession(session), cids);
            return new JsonResult<>(OK,data);
        }
    }
    

    测试

创建订单-前端页面

    确定订单页面中添加点击事件

    $("#btn-create-order").click(function (){
       let aid=$("#address-list").val();
       let cids=location.search.substr(1);
       $.ajax({
          url: "/orders/create",
          type: "get",
          data:"aid="+aid+"&"+cids,
          dataType: "JSON",
          success: function (json){
             if (json.state==200){
                alert("订单创建成功");
                location.href="payment.html";
    
             }
          },
          error: function (xhr){
             alert("购物车收货地址列表数据加载产生未知的异常,"+xhr.message)
          }
       });
    });
    
统计业务方法耗时-AOP

检测项目所有的业务层方法的耗时(开始执行时间和结束执行之差)

切面方法
    切面方法修饰符必须是public。切面方法的返回值可以是void和Object。如果这个方法被@Around注解修饰此方法必须声明为Object类型,反之随意。切面方法的方法名称可以自定义。切面方法可以接收参数,参数是ProccedingloinPoint接口类型的参数。 但是@Aroud所修饰方法必须要传递这个参数,其他随意。
实现相关AOP

    添加依赖

    
        org.aspectj
        aspectjweaver
    
    
        org.aspectj
        aspectjtools
    
    

    定义一个切面类

    @Component//将该类交由spring容器管理
    @Aspect//标记当前类为切面类
    public class TimeAspect {
    }
    

    定义切面方法,使用环绕通知的方式来编写。ProceedingJoinPoint接口表示连接点,目标方法的对象。

    @Component//将该类交由spring容器管理
    @Aspect//标记当前类为切面类
    public class TimeAspect {
        //将当前环绕通知映射到某个页面上(指定连接点)
        @Around("execution(* com.lzj.store.service.impl.*.*(..))")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            //先记录当前时间
            long start = System.currentTimeMillis();
            Object result = pjp.proceed();//调用目标方法
            //后记录当前时间
            long end = System.currentTimeMillis();
            System.out.println("耗时:"+(end-start));
            return result;
        }
    }
    

    将当前环绕通知映射到某个页面上(指定连接点)

    @Around("execution(* com.lzj.store.service.impl.*.*(..))")
    

    启动项目,随机取访问任意功能模块

自己在这基础上添加的功能
    购物车中商品数量的减少购物车中商品的删除购物车页面点击复选框后会显示已选商品数和总价订单页面显示
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/778804.html

Java相关栏目本月热门文章

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号