商城购物后台管理项目

Mall商城后台管理系统——(Vue+SSM) 一、项目背景 在这个互联网高速发展的时代,人们可以享受足不出户的购买商品,只要在家浏览商品下单,几天内就会收到心仪的商品。 Mall商城后台(Vue+SSM)——可做毕业设计 二、项目介绍 Mall商城分为PC端、小程序端、App端、H5端、PC后台,我负责PC后台的开发,Mall商城后台分为基础模块、会员管理、商品管理、订单管理、系统管理、数据统计六大模块 1.基础模块包含登录和退 阅读详情

        好多天都没更了,这两天在老师的帮助下从零到一完成了一个商城购物后台管理的项目,今天刚刚进行项目发布,完结项目。下面来给大家分享一下项目步骤及知识点,顺带再次自己复习一遍。

        由于主攻后端开发的原因,前端页面的搭建只做了解和简单的学习,没有深入去学习,能看懂逻辑即可。

一、模块总览

1.用户登录

用户登录请求数据库进行查询返回标识进行判断是否登陆成功

2.用户管理---用户列表

用户信息

功能属性:id、用户名、电话、邮箱、用户状态、操作(修改、删除)、查找用户、新增用户、翻页跳转功能

3.商品管理---商品分类

商品分类

功能属性:id、分类名称(包含一级分类、二级分类、三级分类)、商品分类状态、标识分类等级、操作(修改、删除)、新增分类(分类名称、父级分类名称)

3.商品管理---商品列表

商品信息

功能属性:id、商品标题、卖点信息、价格、数量、商品状态、操作(修改、删除)、添加商品【(基本信息---商品标题、商品买点、商品价格、商品数量、商品分类信息)、(商品图片---上传图片)、(商品详情---富文本编辑器进行商品的详细描述)】

4.权限管理---shiro框架正在努力学习中

二、用户登录模块

1.数据库的设计

字段:id、username、password、phone、email、status、created、updated

原始数据如图:

密码进行md5算法加密

创建时间以及表的更新时间自动更新

2.实体类的创建

@TableName("user")
@Data
@Accessors(chain = true)
public class User extends BasePojo{
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private String phone;
    private String email;
    private Boolean status;
    @TableField(exist = false)  //该属性不存在
    private Role role;  //定义role角色数据
}
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
	@TableField(fill = FieldFill.INSERT)
	private Date created;	//表示入库时需要赋值
	@TableField(fill = FieldFill.INSERT_UPDATE)
	private Date updated;	//表示入库/更新时赋值.
}

3.返回前端页面参数类的设计实现

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class SysResult implements Serializable {
    private Integer status;//200业务成功   201业务失败
    private String mag;//服务器的提示信息
    private Object data;//封装后台返回值

    public static SysResult fail(){
        return new SysResult(201, "业务执行失败",null);
    }

    public static SysResult fail2(){
        return new SysResult(202, "业务执行失败",null);
    }

    public static SysResult success(){
        return new SysResult(200, "业务执行成功", null);
    }

    public static SysResult success(Object data){
        return new SysResult(200, "业务执行成功", data);
    }

    public static SysResult success(String msg,Object data){
        return new SysResult(200, msg, data);
    }
}

4.控制层的实现

4.1登录---controller

 @PostMapping("/login")
    public SysResult login(@RequestBody User user){
        String token = userService.login(user);
        if(token == null || token.length()==0){
            return SysResult.fail();
        }
        return SysResult.success(token);
    }

 思路:去数据库进行数据比对,相同则返回token,否则返回null

4.2登录---service

@Override
    public String login(User user) {
        //获取明文
        String password=user.getPassword();
        //加密处理
        String md5= DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(md5);
        //获取数据库对象
        User userDB = userMapper.selectOne(new QueryWrapper<User>(user));
        //判断登录是否正常
        if(userDB==null){
            return null;
        }
        //使用UUID动态生成TOKEN,根据当前毫秒数+随机数利用hash算法生成
        //几乎保证不重复2^128
        String token = UUID.randomUUID().toString().replace("-", "");
        return token;
    }

思路:获取对象密码、进行MD5加密之后再次set密码在数据库进行查找,如果没有返回值则登录失败返回null,否则用UUID生成token进行返回

知识点:

  1. 如果数据库里不存在,而实体类中需要额外添加属性应该加注解 @TableField(exist = false) 
  2. 层架结构新增VO层:封装前后端交互数据的媒介
  3. MD5:MD5信息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值,用于确保信息传输完整一致,.理论上MD5不可以被破解的. 只能由明文加密为密文. 不可以反向编译,但是可以用穷举法进行某种意义上的破解
  4. UUID:使用UUID动态生成TOKEN,根据当前时间毫秒数+随机数利用hash算法生成,几乎可以保证不重复.(有2^128的概率hash碰撞)

三、用户列表模块

1.返回前端页面参数类的设计实现

@Data
@Accessors(chain = true)
public class PageResult implements Serializable {
    private String query;  用户查询的数据
    private Integer pageNum;  查询页数
    private Integer pageSize;  查询条数
    private Long total;  查询总记录数
    private Object rows;  分页查询的结果
}

2.用户页面展现控制层的实现

2.1用户页面展现---Controller

 @GetMapping("list")
    public SysResult getUserList(PageResult pageResult){
    pageResult=userService.getUserList(pageResult);
    return SysResult.success(pageResult);
    }

思路:前端传入页数、每页多少条记录、查询关键字,我们添加查询总记录数和分页查询结果进行返回

2.2用户页面展现---service

方法一:手写sql

在mapper里定义抽象方法,在service里进行调用

public interface UserMapper extends BaseMapper<User> {
    //只适用于简单sql,和映射文件二选一a
    @Select("select * from user limit #{start},#{size}")
    List<User> findListByPage(int start, int size);
}
   @Override
    public PageResult getUserList(PageResult pageResult) {
        //1.获取总记录数
        long total = userMapper.selectCount(null);
        //2.获取分页结果
        int size = pageResult.getPageSize();
        int start =(pageResult.getPageNum()-1)* size;
        List<User> userList=userMapper.findListByPage(start,size);
        pageResult.setTotal(total).setRows(userList);
        return pageResult;
    }

思路:在mapper里定义分页sql语句,在service里通过selectCount获取到总记录数,获取记录条数和页数,调用mapper里的方法传入获取集合分别进行set封装并返回。

方法二:MP实现

    @Override
    @Transactional
    public PageResult getUserList(PageResult pageResult) {
        Page<User> page = new Page<>(pageResult.getPageNum(), pageResult.getPageSize());
        boolean x=StringUtils.hasLength(pageResult.getQuery());
        page = userMapper.selectPage(page, new QueryWrapper<User>().like(x, "username", pageResult.getQuery()));
        pageResult.setTotal(page.getTotal()).setRows(page.getRecords());
        return pageResult;
    }

思路:首先进行selectPage分页查询,发现需要两个参数,一个page分页对象,一个条件构造器,这时我们创建分页对象page并传入两个参数,一个页数,一个每页的记录条数。再new条件构造器,这是涉及到了模糊查询。所以在定义一个boolean变量,判断是否传入了查询条件,没有则不执行直接进行分页,最后进行赋值操作并返回

2.3编辑配置类---config

@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
        return interceptor;
    }
}

知识点:

在Mapper里手写sql只适用于简单sql,和映射文件二选一,且注解有select、delete、update、insert

@Transactional注解在更改数据库内容时需要添加。如果后台服务器发生问题,则数据信息应该回滚,而不是提交操作。默认发生运行时异常错误的话会发生事务回滚 。发生编译异常(检查异常),默认规则事务不回滚 。属性说明:noRollbackFor={异常}遇到某种异常不回滚 rollbackFor={}遇到某种异常回滚 。作用: 可以保证数据的 /持久性(永久性)/一致性/隔离性/原子性

API:StringUtils.hasLength()   判断有没有值,有就true,没有就false

编辑配置类:MybatisPlus在执行分页操作时,会被该拦截器拦截,拦截器的作用 动态拼接where条件。主要语句new PaginationInnerInterceptor(DbType.MARIADB));类型是自己所用的数据库,这里是MariaDB

3.用户状态修改

3.1状态修改---controller

   @PutMapping("status/{id}/{status}")
    public SysResult updateStatus( User user){
        userService.updateStatus(user);
        return SysResult.success();
    }

3.2状态修改---service

    @Override
    @Transactional
    public void updateStatus(User user) {
        userMapper.updateById(user);
    }

4.用户新增

4.1用户新增---controller

    @PostMapping("addUser")
    public SysResult addUser(@RequestBody User user){
        userService.addUser(user);
        return SysResult.success();
    }

4.2用户新增---service

    @Override
    @Transactional
    public void addUser(User user) {
        String password = user.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(password)
                .setStatus(true);
                .setCreated(new Date())
                .setUpdated(user.getCreated());
        userMapper.insert(user);
    }

思路:获取密码加密后对user对象进行设置,添加创建时间,添加user对象

5.用户信息更改

5.1用户信息更改---controller

    @GetMapping("/{id}")
    public SysResult getuser(@PathVariable Integer id){
        User user=userService.getuser(id);
        return SysResult.success(user);
    }

    @PutMapping("updateUser")
    public SysResult updateUser(@RequestBody User user){
        userService.updateuser(user);
        return SysResult.success();
    }

思路:进行查询返回user对象,前端拿到user对象进行输入框的双向绑定,用户点击修改,把数据发送给后端进行修改

5.2用户信息更改---service

    @Override
    @Transactional
    public void updateuser(User user) {
        userMapper.updateById(user);
    }

    @Override
    @Transactional
    public User getuser(Integer id) {
        return userMapper.selectOne(new QueryWrapper<User>().eq("id", id));
    }

6.用户信息删除

6.1用户信息删除---controller

    @DeleteMapping("/{id}")
    public SysResult deleteuser( @PathVariable Integer id){
        userService.deleteuser(id);
        return SysResult.success();
    }

6.2用户信息删除---service

    @Override
    @Transactional
    public void deleteuser(Integer id) {
        userMapper.deleteById(id);
    }

思考:写了这么多了,虽然我们写的都是正确的,但是程序总会遇到千奇百怪的错误,那么我们遇到错误又改如何呢?

答:AOP面向切面编程解决

@RestControllerAdvice
public class SystemAOP {
    /*当前的controller层只拦截运行时异常*/
    @ExceptionHandler(RuntimeException.class)
    public SysResult exception(Exception e){
        /*控制台打印异常,必须加*/
        e.printStackTrace();
        return SysResult.fail2();
    }
}

这里只拦截Controller层的异常,这是因为异常会向上抛出,所以其他层的异常都会抛出到controller层,所以只拦截Controller层的异常就可以了,注意异常类型的改变,有异常我们不能暴漏给用户,所以我们返回一个错误信息,前端页面得到信息后进行错误信息选择然后弹出提示信息。

知识点:在完成数据库入库操作时,其中创建时间和修改时间 都是用户必须添加的参数.但是该参数完全由用户填写,显得不智能. 公共的内容应该由框架完成.

添加自动填充实现类:

交给spring来管理

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    /*完成入库操作时填充数据   created/updated*/
    @Override
    public void insertFill(MetaObject metaObject) {
        Date date=new Date();
        this.setFieldValByName("created",date,metaObject);
        this.setFieldValByName("updated", date,metaObject);
    }
    /*完成修改操作时填充数据   updated*/
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updated", new Date(),metaObject);
    }
}

四、商品分类模块

1.数据库的设计

字段:id、parent_id、name、status、level、created、updated

数据库原始数据:

 2.实体类的创建

@TableName("item_cat")
@Data
@Accessors(chain = true)
public class ItemCat extends BasePojo{

    @TableId(type = IdType.AUTO)
    private Integer id;         //定义主键
    private Integer parentId;   //定义父级菜单
    private String name;        //分类名称
    private Boolean status;     //分类状态 0 停用 1 正常
    private Integer level;      //商品分类等级  1 2 3
    @TableField(exist = false)
    private List<ItemCat> children;
}

children:用来封装子集数据

3.分类页面展现

3.1分类页面展现---controller

    @GetMapping("findItemCatList/{level}")
    public SysResult getItemCatList(@PathVariable Integer level){
        List<ItemCat> itemCatList= itemCatService.findItemCatList(level);
        return SysResult.success(itemCatList);
    }

思路:通过等级查询返回分类信息

3.2分类页面展现---service

方式一:

    @Override
    public List<ItemCat> findItemCatList(Integer level) {
        long creattime=System.currentTimeMillis();
        QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", 0);
        List<ItemCat> oneList = itemCatMapper.selectList(queryWrapper);
        for (ItemCat one :
                oneList) {
            queryWrapper.clear();
            queryWrapper.eq("parent_id", one.getId());
            List<ItemCat> twoList=itemCatMapper.selectList(queryWrapper);
            one.setChildren(twoList);
            for (ItemCat two :
                   twoList ) {
                queryWrapper.clear();
                queryWrapper.eq("parent_id", two.getId());
                List<ItemCat> threeList=itemCatMapper.selectList(queryWrapper);
                two.setChildren(threeList);
            }
        }
        long endtime=System.currentTimeMillis();
        System.out.println(endtime-creattime);
        return oneList;
    }

思路:查询父级id为0的集合(一级分类),对集合进行遍历,根据把id作为父级id的条件查询出二级分类封装到children里,再对二级分类遍历,把二级分类的id作为父级id进行查询出三级分类,然后封装到二级分类的children里。这样虽然可行,但是嵌套循环导致大量的查询数据库导致程序变得非常的慢,我们因该减少查询数据库的次数

方式二:

程序设计:
1.数据结构 Map<父级ID,子级列表> 列表信息中不包含嵌套关系
例如: Map<0,一级列表信息> 一级列表不包含二级/三级
Map<一级ID,二级列表信息> 只有2级列表 不包含3级
Map<二级ID,三级列表信息> 只有3级列表信息.
2.根据数据结构动态根据level查询子级.
 

第一步:首先获取所有集合(查询一次数据库)进行分类

private Map<Integer, List<ItemCat>> getMap() {
        Map<Integer,List<ItemCat>> map = new HashMap<>();
        List<ItemCat> list = itemCatMapper.selectList(null);
        for(ItemCat itemCat : list){
            //获取parentId
            int parentId = itemCat.getParentId();
            if(map.containsKey(parentId)){
                //key存在
                map.get(parentId).add(itemCat);
            }else{
                //key不存在
                List<ItemCat> childrenList = new ArrayList<>();
                childrenList.add(itemCat);
                //将第一个元素封装到map中
                map.put(parentId,childrenList);
            }
        }
        return map;
    }

思路:一个方法里封装一个map集合,k是父级id,v是属于父级的儿子的集合,然后查询所有集合,条件为null,进行遍历,获取它们的父级id当作k,如果k存在,则添加到属于这个k的集合中去,如果不存在就创建一个属于这个k的集合把自己添加进去,再添加一个map集合把list集合封装进去,最后返回这个map集合

第二步:封装二级分类

    private List<ItemCat> getTwoList(Map<Integer, List<ItemCat>> map) {
        List<ItemCat> oneList = map.get(0);
        for(ItemCat oneItemCat :oneList){
            int parentId = oneItemCat.getId();
            List<ItemCat> twoList = map.get(parentId);
            oneItemCat.setChildren(twoList);
        }
        return oneList;
    }

思路:一个方法里参数为map集合,先获取一级分类,进行遍历,获取它们的id,在map集合里根据它们的id得出二级分类的集合,再把它们封装到children里

第三步:获取第三级分类

    private List<ItemCat> getThreeList(Map<Integer, List<ItemCat>> map) {
        List<ItemCat> oneList = getTwoList(map);
        for(ItemCat oneItemCat : oneList){
            List<ItemCat> twoList = oneItemCat.getChildren();
            if(twoList == null || twoList.size() == 0){
                System.out.println("执行跳过循环操作");
                continue;
            }
            for (ItemCat twoItemCat : twoList){
                int parentId = twoItemCat.getId();
                List<ItemCat> threeList = map.get(parentId);
                twoItemCat.setChildren(threeList);
            }
        }
        return oneList;
    }

思路:调用获取一级分类的方法,且这时一级分类封装了children,进行遍历,获取集合里每一个的children属性为二级分类集合,进行判断,如果集合为空则跳过本次循环,否则遍历这个二级分类集合,获取它们的id,在map里根据id获取三级分类的集合再封装到二级分类的children里

第四步:总和

@Override
    public List<ItemCat> findItemCatList(Integer level) {
        long startTime = System.currentTimeMillis();

        Map<Integer,List<ItemCat>> map = getMap();
        if(level == 1){
            return map.get(0);
        }
        if(level == 2){
            return getTwoList(map);
        }
        List<ItemCat> list = getThreeList(map);
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:"+(endTime - startTime)+"毫秒");
        return list;
    }

思路:如果参数为1,则直接返回父级id为0的就是一级分类

           如果参数为2,则调用gettwoList方法,传入map集合

           如果参数为3,则调用getThreeList方法,传入map集合

这里可以看到运行速度大大的提高了,以后还会学到redis缓存技术,以后再来和大家分享。

API:map.containsKey(key)  map里判断key是否存在

4.商品分类状态修改

4.1商品分类状态修改---controller

    @PutMapping("status/{id}/{status}")
    public SysResult putstatus(ItemCat itemCat){
        itemCatService.putstatus(itemCat);
        return SysResult.success();
    }

4.2商品分类状态修改---service 

    @Override
    public void putstatus(ItemCat itemCat) {
        itemCatMapper.updateById(itemCat);
    }

5.商品分类新增

5.1商品分类新增---controller

    @PostMapping("saveItemCat")
    public SysResult addItemCat(@RequestBody ItemCat itemCat){
        itemCatService.addItemCat(itemCat);
        return SysResult.success();
    }

5.2商品分类新增---service

@Override
    @Transactional
    public void addItemCat(ItemCat itemCat) {
        itemCat.setStatus(true);
        itemCatMapper.insert(itemCat);
    }

 思路:新增商品状态设置为可用

6.商品分类修改

6.1商品分类修改---controller

    @PutMapping("updateItemCat")
    public SysResult updateItemCat(@RequestBody ItemCat itemCat){
        itemCatService.updateItemCat(itemCat);
        return SysResult.success();
    }

 6.2商品分类修改---service

    @Override
    public void updateItemCat(ItemCat itemCat) {
        itemCatMapper.updateById(itemCat);
    }

7.商品分类删除

7.1商品分类删除---controller

    @DeleteMapping("deleteItemCat")
    public SysResult deleteItemCat(ItemCat itemCat){
        itemCatService.deleteItemCat(itemCat);
        return SysResult.success();
    }

7.2商品分类删除---service

    @Override
    public void deleteItemCat(ItemCat itemCat) {
        int level=itemCat.getLevel();
        if(level==3){
            itemCatMapper.deleteById(itemCat.getId());
        }
        if(level==2){
            itemCatMapper.delete(new QueryWrapper<ItemCat>().eq("parent_id", itemCat.getId()).or().eq("id", itemCat.getId()));
        }
        if(level==1){
            QueryWrapper<ItemCat> queryWrapper= new QueryWrapper<ItemCat>()
                    .eq("parent_id", itemCat.getId());
            //获取二级id集合
            List twoids = itemCatMapper.selectObjs(queryWrapper);
            //删除三级的数据
            queryWrapper.clear();
            queryWrapper.in(twoids.size()>0,"parent_id", twoids)
                        .or()
                        .eq("parent_id",itemCat.getId())
                        .or()
                        .eq("id", itemCat.getId());
            itemCatMapper.delete(queryWrapper);
        }
        }

思路:如果要删除的不是最后一级,那么要把它的子级也删除掉,我们根据等级来做删除操作,如果判断要删除的是第三级,那么直接获取id删除即可;如果要删除的是二级分类,那么应该删除它的三级子级,根据二级的id作为三级的父类id进行删除三级,再删除二级;如果删除的是一级分类,那么应该把自己的id作为父类id获取到二级分类的id集合,判断二级集合是否为空,不为空的话设置为条件构造器的in条件,或者等于二级id,或者等于自己本身。然后调用删除方法删除这个条件构造器。

五、商品列表模块

1.数据库的设计

字段:id、title、sell_point、price、num、images、item_cat_id、status、created、updated

数据库原始数据:

 2.实体类的创建

@TableName("item")
@Data
@Accessors(chain = true)
public class Item extends BasePojo{
    @TableId(type = IdType.AUTO)
    private Integer id;         //商品Id号
    private String title;       //商品标题信息
    private String sellPoint;   //卖点信息
    private Integer price;      //商品价格
    private Integer num;        //商品数量
    private String images;       //商品图片
    private Integer itemCatId;  //商品分类ID号
    private Boolean status;     //状态信息    0 下架 1 上架
}

3.页面展现

3.1页面展现---controller

    @GetMapping("getItemList")
    public SysResult getItemList(PageResult pageResult){
        pageResult=itemService.getItemList(pageResult);
        return SysResult.success(pageResult);
    }

3.2页面展现---service

    @Override
    @Transactional
    public PageResult getItemList(PageResult pageResult) {
        Page<Item> page = new Page<>(pageResult.getPageNum(), pageResult.getPageSize());
        boolean x= StringUtils.hasLength(pageResult.getQuery());
        page=itemMapper.selectPage(page,new QueryWrapper<Item>().like(x, "title",pageResult.getQuery()));
        pageResult.setTotal(page.getTotal()).setRows(page.getRecords());
        return pageResult;
    }

思路:此分页实现逻辑同商品分类分页

4.删除商品 

4.1删除商品---controller

    @DeleteMapping("deleteItemById")
    public SysResult deleteItemById(Item item){
        itemService.deleteItemById(item);
        return SysResult.success();
    }

4.2删除商品---service

    @Override
    public void deleteItemById(Item item) {
        itemDescMapper.deleteById(item);
        itemMapper.deleteById(item);
    }

5.商品状态修改

5.1商品状态修改---controller

    @PutMapping("updateItemStatus")
    public SysResult updateItemStatus(@RequestBody Item item){
        itemService.updateItemStatus(item);
        return SysResult.success();
    }

5.2 商品状态修改---service

    @Override
    @Transactional
    public void updateItemStatus(Item item) {
        itemMapper.updateById(item);
    }

6.修改商品属性

6.1修改商品属性---controller

    @PutMapping("updateItem")
    public SysResult updateItem(@RequestBody Item item){
        itemService.updateItem(item);
        return SysResult.success();
    }

6.2修改商品属性---service

    @Override
    public void updateItem(Item item) {
        itemMapper.updateById(item);
    }

7.添加商品

7.1.添加商品VO类的创建

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ItemVO {  //该对象封装商品所有的参数信息
    private Item item;
    private ItemDesc itemDesc;
    private ItemParam itemParam;
}

7.2添加商品ItemDesc类的创建

@TableName("item_desc")
@Data
@Accessors(chain = true)
public class ItemDesc extends BasePojo{
    //item和itemDesc一对一id相同
    //简化了外键约束
    @TableId
    private Integer id;
    private String itemDesc;
}

7.3ItemParam类的创建

@TableName("item_param")
@Data
@Accessors(chain = true)
public class ItemParam extends BasePojo{
    private Integer id;
    private String dynamicArgs;
    private String staticArgs;
    //为了实现参数封装添加的数据
    @TableField(exist = false)
    private ItemParamVO[] dynamicArray;
    @TableField(exist = false)
    private ItemParamVO[] staticArray;
}

7.4添加商品页面展现---controller

    @PostMapping("saveItem")
    public SysResult saveItem(@RequestBody ItemVO itemVO){
        itemService.saveItem(itemVO);
        return SysResult.success();
    }

7.5添加商品页面展现---servicec

    @Override
    @Transactional
    public void saveItem(ItemVO itemVO) {
        //item主键自增,数据入库之后才会有主键
        Item item = itemVO.getItem();
        item.setStatus(true);
        itemMapper.insert(item);
        //入库之后才有主键,现在为null
        //MP自动将主键的回显功能实现
        //itemId和ItemDescId是一样的
        ItemDesc itemDesc = itemVO.getItemDesc();
        itemDesc.setId(item.getId());
        itemDescMapper.insert(itemDesc);
    }

思路:详情信息和商品页面展现信息是分开的,解耦,缓和数据库压力,提高页面展现速度,所以商品的id==商品详情的id,商品入库之后回显id,再把id赋值给商品详情,商品详情再次入库。(商品详情存的都是大字段,存储html的代码片段)

8.文件上传

8.1文件上传VO类创建

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO implements Serializable {
    private String virtualPath;//虚拟地址
    private String urlPath;//url地址
    private String fileName;//文件名称
}

8.2文件上传---controller

@RestController
@RequestMapping("file")
@CrossOrigin
public class FileController {
    @Autowired
    private FileService fileService;
    @PostMapping("upload")
    public SysResult upload(MultipartFile file) throws IOException {
        ImageVO imageVO=fileService.upload(file);
        if(imageVO==null){//业务执行有误
            return SysResult.fail();
        }
        return SysResult.success(imageVO);
    }

思路:把文件传入,如果业务层返回null则出错,否则直接返回imageVO对象

8.3文件上传---service

@Service
@PropertySource("classpath:/image.properties")//放在配置文件里进行配置  和@value合用
public class FileServiceimpl implements FileService{
    @Value("${image.localDir}")
    private String localDir;  //="D:/images";
    @Value("${image.preUrl}")
    private String preUrl;     //="http://image.jt.com";

    @Override
    public ImageVO upload(MultipartFile file) {
        //完成校验:
        //1.获取图片名称
        String filename = file.getOriginalFilename();
        //bug:windows不区分大小写,所以我们全部变小写
        filename = filename.toLowerCase();
        //正则
        if(!filename.matches("^.+\\.(jpg|png|gif)$")){
            //不是图片返回null
            return null;
        }
        //2.判断是否满足图片固有属性
        try {
            BufferedImage bufferedImage=ImageIO.read(file.getInputStream());
            int width=bufferedImage.getWidth();
            int height=bufferedImage.getHeight();
            if(width==0||height==0){
                return null;
            }
            //3.为了提高查询效率,分目录存储   时间--yyyy/MM/dd
            String dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
            String localDirPath=localDir+dateDir;
            //创建目录
            File dirFile = new File(localDirPath);
            if(!dirFile.exists()){
                dirFile.mkdirs();
            }

            //4.使用UUID替换文件名称
            //唯一:系统内部唯一
            String uuid= UUID.randomUUID().toString().replace("-", "");
            //截取文件的后缀名
            int index=filename.lastIndexOf(".");
            String fileType=filename.substring(index);
            String newFileName=uuid+fileType;
            //5.文件上传   目录/文件名称
            String realFilePath=localDirPath+newFileName;
            file.transferTo(new File(realFilePath));
            System.out.println("文件上传成功");
            //6.封装返回值
            String virtualPath=dateDir+newFileName;
            //自定义url
            String url=preUrl+virtualPath;
            System.out.println("磁盘地址"+realFilePath);
            System.out.println("网络地址"+url);
            return new ImageVO(virtualPath,url,newFileName);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

思路:首先获取图片名称,为了后面的验证,我们先全部变成小写,进行正则验证是否符合图片格式,再次检查是不是符合图片格式的有害软件伪装的通过获取图片固有属性判断图片的长和宽,定义存放图片文件的地址用来存放图片,提取地址头与用时间分隔命名的文件夹拼接形成每天存放图片子文件目录,先判断是否存在,不存在就创建,为了防图片名称相同,我们用UUID来重命名图片名称,然后进行文件上传,进行封装虚拟路径封装自定义的url进行返回,这里用到了nginx反向代理技术技术,我们后面再说,如果上述有步骤出错,那么catch返回null,前端输出执行出错。

知识点:

API:

  1. MultipartFile输入流类,可以自动关流
  2. file.getOriginalFilename()获取图片名称
  3. toLowerCase()全部变小写
  4. filename.matches("^.+\\.(jpg|png|gif)$")正则判断
  5. BufferedImage bufferedImage=ImageIO.read(file.getInputStream());图片固有属性
  6. String dateDir = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());定义时间规则
  7. lastIndexOf从后向前数,第一个参数是到什么位置
  8. file.transferTo(new File(realFilePath));文件上传,参数是目录加文件名称

动态定义全局变量:

image.properties配置文件里  K=V格式

image.localDir=/usr/local/src/images
image.preUrl=http://image.jt.com

Service类里:

在类头上加:@PropertySource("classpath:/image.properties")参数后面是配置文件名

在成员变量上加:@Value("${image.localDir}") 这是配置文件里K的写法,把V赋值给下面的成员变量

常用正则表达式:

详细请看:正表达式语法大全

Nginx反向代理服务:项目结束和发布一起总结

9.图片删除

9.1图片删除---controller

    @DeleteMapping("deleteFile")
    public SysResult deleteFile(String virtualPath){
        fileService.deleteFile(virtualPath);
        return SysResult.success();
    }

9.2图片删除---service

    @Override
    public void deleteFile(String virtualPath) {
        String delepath=localDir+virtualPath;
        File file = new File(delepath);
        if(file.exists()){
            file.delete();
        }
    }

        到这里这个不太完整的项目就结束了,大部分 都是一些基本的CRUD,知识根据具体的业务不同做一些逻辑的操作,这也是前面所学的一些综合,权限还没有做,因为我还在学习shiro中,希望能学的会吧,明天说一下Nginx和linux的项目部署发布!

这两天忙的要死啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

满怀希望就会所向披靡!!!

        

电商后台管理系统-商品管理篇 本项目是以自有电商为背景,自有电商最基础的几大功能应该包涵:渠道搭建能力、店铺装修能力、商品管理能力、订单处理能力、活动配置能力、用户管理能力。其中,以商品管理最为基础,因为没有商品就不能装修店铺,亦不存在订单、用户,因此本次需求文档仅涉及商品信息管理的详细需求分析文档,因此与商品管理本身关联不大的地方,逻辑或有缺少。 阅读详情

相关推荐

大型分布式Java项目--"宜立方商城"

一.项目简介 宜立方商城是一个综合性的B2C平台,架构模仿京东商城、天猫商城,该平台主要提供两种服务:一,买家可以在商城浏览商品、下订单,以及参加各种活动。二,管理员,运营可以在平台后台管理系统中管理商品、订单、会员等 宜立方商城项目使用Java的SSM框架处理后端业务,同时使用了Dubbo框架,是一个分布式支持高并发的商城项目。数据库选择MySQL集群,缓存使用Redis集群,使用Solr框架实...

CycloneKid的博客 3988

在线商城和商城后台管理系统

在线商城和商城后台管理系统 1、商城使用了vue2+elementUI 实现了登录注册、浏览商品、购买商品、收藏商品、商品搜索、加入购物车、查看订单、添加收货地址、在线客服、分类查看商品的等功能 2、商城后台管理系统使用了react+antd 实现了注册登录、商城整体数据的查看,用户管理、商品管理、订单管理、财务管理、客服服务的功能,其下还分各种小功能对用户商品订单等详细功能进行增删改查的操作 2、商城的后端使用了express+mongoDB 使用了node.js的express和mongoDB数据库

weixin_45892086的博客 9486

【商城实战(19)】用户管理:打造强大后台管理系统

本文聚焦商城后台用户管理功能开发。开篇强调其在商城运营中的核心地位,随后详细阐述实现过程。前端用 Element Plus 搭建用户列表展示、编辑删除及角色权限分配页面,涵盖面包屑导航、搜索筛选栏等组件运用及交互逻辑。后端基于 Spring Boot 开发,创建实体类、Mapper 接口等实现数据查询、编辑删除及角色权限相关功能。最后总结成果,从性能优化、功能拓展、安全性增强三方面对未来提出展望,助力开发者搭建高效稳定的用户管理系统。

邓邓子的博客 1041

微信小程序商城 (后台JAVA)

技术选型 1 后端使用技术 <ul><li style="margin-left:0px;">1.1 springframework4.3.7.RELEASE</li> <li style="margin-left:0px;">1.2 mybatis3.4.1</li> <li style="margin-left:0px...

Rsun2018的博客 4006

HTML5购物商城后台管理响应式模板项目

在当今数字时代,拥有一个能够快速响应用户需求并适应多种设备的后台管理系统是至关重要的。HTML5购物商城系统后台管理响应式模板正是一款集成了最新前端技术的设计解决方案,旨在为电子商务运营提供一个高效、直观、且用户友好的管理平台。本章节将概述HTML5购物商城系统后台管理响应式模板的核心功能与设计优势,为读者提供一个全面的了解该模板如何在竞争激烈的市场中脱颖而出的窗口。

weixin_31776191的博客 1025

Windows Server远程桌面断开后,PyWinAuto/按键精灵脚本失效?试试这个‘会话保持’的终极配置

本文深入解析Windows Server远程桌面断开后导致PyWinAuto/按键精灵脚本失效的问题,提供会话保持的终极配置方案。通过转换到控制台会话(console模式)和调整组策略,确保自动化程序在断开连接后持续稳定运行,适用于Windows Server各版本。

weixin_28738165的博客 227

JavaWeb网上商城简易后台管理项目:轻松管理商品,提升购物体验

JavaWeb网上商城简易后台管理项目:轻松管理商品,提升购物体验 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 作为一款功能全面的Java Web解决方案,JavaWeb网上商城简易后台管理项目为您提供了一套高效、稳定的网上商城管理方案。该项目不仅涵盖了商品管理的核心功能,还实现了用户购物体验的优化,是二次开发、教学演示和个人项目的理想选择。 项目技术分析 技术栈 ...

gitblog_06764的博客 413

电子数据取证实战:从“平航杯”竞赛看计算机检材分析的关键技术与案例解析

本文通过'平航杯'电子数据取证竞赛案例,深入解析计算机检材分析的关键技术,包括USB设备追踪、浏览器历史分析、AI换脸取证、虚拟货币交易追踪等实战技巧。重点探讨了数字痕迹的发现与关联,以及电子取证工具链的应用,为从业人员提供实用的技术参考和案例分析。

www00的博客 537

JAVAWEB购物商城项目

JAVAWEB购物商城项目 【下载地址】JAVAWEB购物商城项目 这是一个专为JavaWeb初学者设计的购物商城项目,采用Servlet和MySQL作为核心开发环境,集成了C3P0数据源、MVC设计模式、动态代理模式等主流技术,并运用Ajax、jQuery、JSP等工具实现丰富的交互功能。项目涵盖了商品展示、搜索、详情...

gitblog_06731的博客 416

SIwave仿真手册——信号完整性仿真之S参数的提取(二)

上一篇简单了讲述了软件的各个操作界面,本次博客更新关于的仿真,因为第二章完整的内容比较多,所以分成好几篇博客进行分享。

luohuo9844的博客 1万+

秦时明月6.2魔改版_从零到一部署指南_含安卓客户端配置与GM后台直通

本文详细介绍了秦时明月6.2魔改版的从零到一部署指南,包括环境准备、服务端安装、安卓客户端配置以及GM后台直通等关键步骤。特别针对安卓客户端配置和GM工具使用提供了实用技巧,帮助用户快速搭建并优化游戏服务器,确保稳定运行。

weixin_42522964的博客 317

【亲测免费】 基于JAVAWEB购物商城项目

基于JAVAWEB购物商城项目 去发现同类优质开源项目:https://gitcode.com/ 本项目是一个面向初学者的JavaWeb学习项目,通过实现一个简单的在线购物商城,帮助初学者理解和掌握JavaWeb开发的相关技术和业务逻辑。 项目描述 本项目采用Servlet和MySQL作为主要开发环境,运用C3P0数据源、MVC设计模式、动态代理模式、Ajax、jQuery、JSP、JSTL、Fi...

gitblog_06714的博客 571

PCIe学习笔记(一)-------1.3 PCIe数据包(TLP,DLLP,PLP)

1,PCIe的层次结构 PICe设备在硬件电路上可以分为三个层次结构:事务层(Transaction Layer)、数据链路层(Data Link Layer)、物理层(Physical)。每一个层次又可以分为两个部分,分别为处理发送和处理接收的部分。 也可以分为四个层次,将事务层以上的层次称为软件层(Software Layer)。 2,PCIe数据包 前面已经说过PCIe之间是以数据包的形式来传输数据的,协议共定义了3类数据包。与处理层相关的是处理层数据包(TLP),与数据链路层相关的是数据

llxxyy507的博客 2万+

jsp ssm 网上购物系统 在线购物 在线商城平台 项目源码 web java

一、项目简介本项目是一套基于SSM的网上购物系统 ,主要针对计算机相关专业的和需要项目实战练习的Java学习者。包含:项目源码、数据库脚本、软件工具等。项目都经过严格调试,确保可以运行!二、技术实现​后端技术:Spring、SpringMVC、MyBatis前端技术:JSP、HTML、CSS、JavaScript、jQuery、BootStrap​数据库:MySQL系统架构:MVC、B/S开发环境:JDK、Eclipse或IDEA、Tomcat、MySQL三、系统功能。

10年大厂开发经验,项目资料、毕设指导和技术答疑,欢迎咨询交流! 413

onnxruntime模型部署(一)-pythonAPI

简单模型的onnx模型的导出,以及onnxruntime推理引擎的模型部署

qq_45983373的博客 4059

UE5入门第二周总结:基于蓝图的国际象棋的实现

在C++的学习中,这是一个入门级别的项目。我们的目标是灵活运用蓝图的特性和强大的可视化功能,做出这一入门级项目的同时,思考如何优化并改进算法,并尽可能的用面向对象的方法实现,用蓝图中的继承和多态简化编码过程。

YoungeYang的博客 1262

基于javaweb和mysql的jsp+servlet在线商城购物商城服装商城(前台、后台)(java+jsp+servlet+mysql)

基于javaweb和mysql的jsp+servlet在线商城购物商城服装商城(前台、后台)(java+jsp+servlet+mysql)基于javaweb的JSP+Servlet在线商城购物商城服装商城(前台、后台)(java+jsp+servlet+mysql)"alert('验证码错误');"alert('修改失败');"alert('请先登录');"alert('请先登录');"alert('添加失败');"alert('修改失败');"alert('修改失败');

欢迎私信 711

华为自研 ERP、Oracle EBS 与 SAP 上线时系统配置思路设计差异深度分析

本文对比分析了华为自研ERP、Oracle EBS和SAP S/4HANA三大ERP系统在上线配置思路上的核心差异。从架构设计看,华为采用分布式微服务架构,SAP采用分层服务架构,Oracle采用集中式一体化架构;在模块配置上,华为以业务场景为导向,SAP基于行业模板,Oracle强调模块独立性;数据迁移方面,华为侧重业务规则清洗,SAP注重数据质量监控,Oracle依赖标准化工具;流程适配上,华为系统随流程调整,SAP在框架内优化,Oracle需定制开发;定制开发模式差异明显,华为采用插件化扩展,SAP使

金牌架构师 1455

计算机毕设项目源码之微信小程序服装购物商城+后台管理系统

本系统包含微信小程序做的服装商城前台和Java做的后台管理系统:微信小程序——服装商城前台涉及技术:WXML 和 WXSS、JavaScriptJava——服装商城后台涉及技术:前端使用技术:JSP,HTML5,CSS3、JavaScript、jQuery、bootstrap等后台使用技术:Spring、SpringMvc、Mybatis(SSM)等数据库:Mysql数据库。

weixin_40057430的博客 350

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

基于Vue的PC端综合性网上购物商城

基于Vue的PC端综合性网上购物商城 【下载地址】基于Vue的PC端综合性网上购物商城 本项目是一个基于Vue的PC端综合性网上购物商城,涵盖了前台商城和后台管理系统。后端采用Koa2框架,数据库使用MySQL进行数据存储。该项目旨在提供一个完整的电商解决方案,适用于开发者和企业在PC端搭建自己的网上购物平台 ...

gitblog_09706的博客 841

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

计算机毕业设计Java毕设项目之微信小程序服装购物商城+后台管理系统

微信小程序服装商城+后台管理系统》该项目含有源码、文档等资料、配套开发软件、软件安装教程、项目发布教程等本系统包含微信小程序做的服装商城前台和Java做的后台管理系统:微信小程序——服装商城前台涉及技术:WXML 和 WXSS、JavaScriptJava——服装商城后台涉及技术:前端使用技术:JSP,HTML5,CSS3、JavaScript、jQuery、bootstrap等后台使用技术:Spring、SpringMvc、Mybatis(SSM)等数据库:Mysql数据库。

weixin_40057430的博客 195

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

基于Java的网上体育商城的设计与实现

本系统的设计严格遵循软件开发流程,使用MVC模式,采用开源框架Struts2、Spring、Hibernate进行开发,使用java语言编写,后端的数据库采用MySQL,通过Spring配置文件与数据库进行无缝连接,应用可以跨平台的Web应用技术与SSH结合开发的网上购物系统—网上体育商城,通过使用这些框架,使开发过程中的逻辑更加清晰,更有层次,也简化了代码的书写,更好的了解了Web应用程序和这些开源的框架技术,熟练了使用框架开发的流程。

znzbs的博客 150

基于Python购物商城系统 Flask购物商城 在线购物 购物商城网站 MySQL数据库 订单管理 后台管理 计算机毕业设计(源码+文档)✅

基于Python购物商城系统 Flask购物商城 在线购物 购物商城网站 MySQL数据库 订单管理 后台管理 计算机毕业设计(源码+文档)✅

公众号:源码之屋,十多年程序猿资深互联网人,目前专注于Python/Java/大数据项目解决方案制定,提供各行业各编程语言的全套开发服务,喜爱code,喜爱分享,生命不止,编码不息! 923

计算机毕业设计项目之微信小程序服装购物商城+后台管理系统

本系统包含微信小程序做的服装商城前台和Java做的后台管理系统:微信小程序——服装商城前台涉及技术:WXML 和 WXSS、JavaScriptJava——服装商城后台涉及技术:前端使用技术:JSP,HTML5,CSS3、JavaScript、jQuery、bootstrap等后台使用技术:Spring、SpringMvc、Mybatis(SSM)等数据库:Mysql数据库。

weixin_45776178的博客 342

java网上商城 购物网站 在线商品销售系统设计与实现 项目源代码下载 javaweb jsp ssm ssh mysql

JAVA部落网上商城系统,在线演示地址:http://www.javabuluo.com 1 前端商城界面 1.1 商城首页 首页分为三个部分,分别显示热销推荐、特价促销、新品上架三个推荐类目的商品列表 以上这三类推荐商品可以分别在后台管理中设置 点击商品图片或商品标题可以进入商品详情页面 点击【加入购物车】按钮会将次商品加入购物车 ...

JAVA部落 1460
上一篇: 页面登录知识
下一篇: Nginx反向正向代理
希望不要秃头的0.0
博客等级 码龄6年 64粉丝 39原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值