商品管理模块
文章目录
- 商品管理模块
- 一、分页查询商品
- 1.1 实体类
- 1.1.1 Store 仓库
- 1.1.2 Brand 品牌
- 1.1.3 ProductType 商品分类
- 1.1.4 Supply 供应商
- 1.1.5 Place 产地
- 1.1.6 Unit 单位
- 1.1.7 Product 商品
 
- 1.2 查询所有仓库
- 1.2.1 Mapper
- 1.2.2 Service
- 1.2.3 Controller
- 1.2.4 效果图
 
- 1.3 查询所有品牌
- 1.3.1 Mapper
- 1.3.2 Service
- 1.3.3 Controller
- 1.3.4 效果图
 
- 1.4 分页查询商品
- 1.4.1 Mapper
- 1.4.2 Service
- 1.4.3 Controller
- 1.4.4 效果图
 
 
- 二、添加商品
- 2.1 查询所有分类树
- 2.1.1 Mapper
- 2.1.2 Service
- 2.1.3 Controller
- 2.1.4 效果图
 
- 2.2 查询所有供应商
- 2.2.1 Mapper
- 2.2.2 Service
- 2.2.3 Controller
- 2.2.4 效果图
 
- 2.3 查询所有产地
- 2.3.1 Mapper
- 2.3.2 Service
- 2.3.3 Controller
- 2.3.4 效果图
 
- 2.4 查询所有单位
- 2.4.1 Mapper
- 2.4.2 Service
- 2.4.3 Controller
- 2.4.4 效果图
 
- 2.5 上传图片
- 2.5.1 Mapper
- 2.5.2 Service
- 2.5.3 Controller
- 2.5.4 效果图
 
- 2.6 添加商品
- 2.6.1 Mapper
- 2.6.2 Service
- 2.6.3 Controller
- 2.6.4 效果图
 
 
- 三、修改商品
- 3.1 上下架状态
- 3.1.1 Mapper
- 3.1.2 Service
- 3.1.3 Controller
 
- 3.2 修改商品
- 3.2.1 Mapper
- 3.2.2 Service
- 3.2.3 Controller
 
 
- 四、删除商品
- 4.1 Mapper
- 4.2 Service
- 4.3 controller
 
- 五、添加采购单(核心)
- 5.1 采购流程
- 5.2 采购单实体类
- 5.3 添加采购单
- 5.3.1 Mapper
- 5.3.2 Service
- 5.3.3 Controller
- 5.3.4 效果图
 
 
- 六、添加出库单(核心)
- 6.1 出库流程
- 5.2 出库单实体类
- 5.3 添加出库单
- 5.3.1 Mapper
- 5.3.2 Service
- 5.3.3 Controller
- 5.3.4 效果图
 
 
- 七、商品分类
- 7.1 查询商品分类树
- 7.1.1 Controller
- 7.1.2 效果图
 
- 7.2 添加商品分类
- 7.2.1 Mapper
- 7.2.2 Service
- 7.2.3 Controller
- 7.2.4 效果图
 
- 7.3 删除商品分类
- 7.3.1 Mapper
- 7.3.2 Service
- 7.3.3 Controller
 
- 7.4 修改商品分类
- 7.4.1 Mapper
- 7.4.2 Service
- 7.4.3 Controller
- 7.4.4 效果图
 
 
一、分页查询商品

1.1 实体类
1.1.1 Store 仓库
/**
 * 仓库表store表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Store implements Serializable {
    private static final long serialVersionUID = 4011912702717598523L;
   
    private Integer storeId;//仓库id
    private String storeName;//仓库名称
    private String storeNum;//仓库编码
    private String storeAddress;//仓库地址
    private String concat;//仓库联系人
    private String phone;//仓库联系电话
}

1.1.2 Brand 品牌
/**
 * 品牌表brand表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Brand implements Serializable {
    private Integer brandId;//品牌id
    private String brandName;//品牌名称
    private String brandLeter;//品牌首字母
    private String brandDesc;//品牌描述
}

1.1.3 ProductType 商品分类
/**
 * 商品分类表product_type表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class ProductType implements Serializable {
    private Integer typeId;//分类id
    private Integer parentId;//上级分类id
    private String typeCode;//分类代码
    private String typeName;//分类名称
    private String typeDesc;//分类描述
    //自定义List<ProductType>集合属性,用于存储当前分类的所有子级分类
    private List<ProductType> childProductCategory;
}

1.1.4 Supply 供应商
/**
 * 供应商表supply表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Supply implements Serializable {
    private Integer supplyId;//供应商id
    private String supplyNum;//供应商代码
    private String supplyName;//供应商名称
    private String supplyIntroduce;//供应商介绍
    private String concat;//供应商联系人
    private String phone;//供应商联系电话
    private String address;//供应商地址
    private String isDelete;//是否删除状态,0未删除,1删除
}

1.1.5 Place 产地

/**
 * 产地表place表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Place implements Serializable {
    private Integer placeId;//产地id
    private String placeName;//产地名称
    private String placeNum;//产地代码
    private String introduce;//产地介绍
    private String isDelete;//是否删除状态,0未删除,1删除
}
1.1.6 Unit 单位
/**
 * 单位表unit表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Unit implements Serializable {
    private Integer unitId;//单位id
    private String unitName;//单位
    private String unitDesc;//单位描述
}

1.1.7 Product 商品
/**
 * 商品表product表对应的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Product {
    private Integer productId;//商品id
    private Integer storeId;//商品所在仓库id
    private String storeName;//非表中字段 --商品所在仓库名称
    private Integer brandId;//商品所属品牌id
    private String brandName;//非表中字段 -- 商品所属品牌名称
    private String productName;//商品名称
    private String productNum;//商品编码
    private Integer productInvent;//商品库存
    private Integer typeId;//商品所属分类id
    private String typeName;//非表中字段 -- 商品所属分类名称
    private Integer supplyId;//商品供应商id
    private String supplyName;//非表中字段 -- 商品供应商名称
    private Integer placeId;//商品产地id
    private String placeName;//非表中字段 -- 商品产地名称
    private Integer unitId;//商品单位id
    private String unitName;//非表中字段 -- 商品单位名称
    private String introduce;//商品介绍
    private String upDownState;//商品上下架状态,1.上架,0.下架
    private Double inPrice;//商品进价
    private Double salePrice;//商品售价
    private Double memPrice;//商品会员价
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;//商品的创建时间
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;//商品的修改时间
    private Integer createBy;//创建商品的用户id
    private Integer updateBy;//修改商品的用户id
    private String imgs;//商品的图片地址
    @JsonFormat(pattern="yyyy-MM-dd")
    private Date productDate;//商品的生产日期
    @JsonFormat(pattern="yyyy-MM-dd")
    private Date suppDate;//商品的保质期
    private String isOverDate;//非表中字段 -- 商品是否过期,0未过期,1已过期
}

1.2 查询所有仓库
为搜索商品仓库下拉框组装数据
1.2.1 Mapper
//  查询所有仓库的方法
    public List<Store> findAllStore();
<select id="findAllStore" resultType="com.pn.entity.Store">
    select *
    from store
</select>
1.2.2 Service
@CacheConfig(cacheNames = "com.pn.service.impl.StoreServiceImpl")
@Service
public class StoreServiceImpl implements StoreService {
    @Autowired
    private StoreMapper storeMapper;
    @Cacheable(key = "'all:store'")
    @Override
    public List<Store> queryAllStore() {
        return storeMapper.findAllStore();
    }
}
1.2.3 Controller
@Slf4j
@RestController
@RequestMapping("/product")
public class ProductController {
//  注入StoreService
    @Autowired
    private StoreService storeService;
    @RequestMapping("/store-list")
    public Result storeList(){
       return Result.ok(storeService.queryAllStore()) ;
    }
}
1.2.4 效果图

1.3 查询所有品牌
1.3.1 Mapper
//  查询所有品牌的方法
    public List<Brand> findAllBrand();
<select id="findAllBrand" resultType="com.pn.entity.Brand">
    select *
    from brand
</select>
1.3.2 Service
@CacheConfig(cacheNames = "'com.pn.service.impl.BrandServiceImpl'")
@Service
public class BrandServiceImpl implements BrandService {
    @Autowired
    private BrandMapper brandMapper;
    @Cacheable(key = "'all:brand'")
    @Override
    public List<Brand> queryAllBrand() {
        return brandMapper.findAllBrand();
    }
}
1.3.3 Controller
@CacheConfig(cacheNames = "com.pn.service.impl.BrandServiceImpl")
@Service
public class BrandServiceImpl implements BrandService {
    @Autowired
    private BrandMapper brandMapper;
    @Cacheable(key = "'all:brand'")
    @Override
    public List<Brand> queryAllBrand() {
        return brandMapper.findAllBrand();
    }
}
1.3.4 效果图

1.4 分页查询商品
根据仓库 商品名称 品牌 类型 供应商 产地 上下架状态 过期与查询商品并分页

1.4.1 Mapper
//  查询商品行数的方法
    public Integer findProductRowCount(Product product);
//  分页查询商品的方法
    public List<Product> findProductPage(@Param("page") Page page, @Param("product") Product product);
<select id="findProductRowCount" resultType="java.lang.Integer">
    select count(*) from
    product t1, store t2, brand t3, product_type t4, supply t5,
    place t6, unit t7
    where t1.store_id = t2.store_id and t1.brand_id = t3.brand_id
    and t1.type_id = t4.type_id and t1.supply_id = t5.supply_id
    and t1.place_id = t6.place_id and t1.unit_id = t7.unit_id
    <if test="storeId != null">
        and t1.store_id = #{storeId}
    </if>
    <if test="productName != null and productName != ''">
        and t1.product_name like concat('%',#{productName},'%')
    </if>
    <if test="brandName != null and brandName != ''">
        and t3.brand_name like concat('%', #{brandName}, '%')
    </if>
    <if test="typeName != null and typeName != ''">
        and t4.type_name like concat('%', #{typeName}, '%')
    </if>
    <if test="supplyName != null and supplyName != ''">
        and t5.supply_name like concat('%', #{supplyName}, '%')
    </if>
    <if test="placeName != null and placeName != ''">
        and t6.place_name like concat('%', #{placeName}, '%')
    </if>
    <if test="upDownState != null and upDownState != ''">
        and t1.up_down_state = #{upDownState}
    </if>
    <!--
      如果方法参数Product对象的isOverDate属性值为1(查询已过期商品),则查询
      当前时间大于product表中supp_date列的时间的商品;
      反之如果方法参数Product对象的isOverDate属性值为0(查询未过期商品),则
      查询当前时间小于product表中supp_date列的时间的商品;
    -->
    <if test="isOverDate != null and isOverDate != '' and isOverDate==1">
        and date_format(now(), '%y-%m-%d') > t1.supp_date
    </if>
    <if test="isOverDate != null and isOverDate != '' and isOverDate==0">
        and date_format(now(), '%y-%m-%d') < t1.supp_date
    </if>
</select>
<select id="findProductPage" resultType="com.pn.entity.Product">
    select t1.*, t2.store_name, t3.brand_name, t4.type_name,
    t5.supply_name, t6.place_name, t7.unit_name from
    product t1, store t2, brand t3, product_type t4, supply t5,
    place t6, unit t7
    where t1.store_id = t2.store_id and t1.brand_id = t3.brand_id
    and t1.type_id = t4.type_id and t1.supply_id = t5.supply_id
    and t1.place_id = t6.place_id and t1.unit_id = t7.unit_id
    <if test="product.storeId != null">
        and t1.store_id = #{product.storeId}
    </if>
    <if test="product.productName != null and product.productName != ''">
        and t1.product_name like concat('%',#{product.productName},'%')
    </if>
    <if test="product.brandName != null and product.brandName != ''">
        and t3.brand_name like concat('%', #{product.brandName}, '%')
    </if>
    <if test="product.typeName != null and product.typeName != ''">
        and t4.type_name like concat('%', #{product.typeName}, '%')
    </if>
    <if test="product.supplyName != null and product.supplyName != ''">
        and t5.supply_name like concat('%', #{product.supplyName}, '%')
    </if>
    <if test="product.placeName != null and product.placeName != ''">
        and t6.place_name like concat('%', #{product.placeName}, '%')
    </if>
    <if test="product.upDownState != null and product.upDownState != ''">
        and t1.up_down_state = #{product.upDownState}
    </if>
    <if test="product.isOverDate != null and product.isOverDate != '' and product.isOverDate==1">
        and date_format(now(), '%y-%m-%d') > t1.supp_date
    </if>
    <if test="product.isOverDate != null and product.isOverDate != '' and product.isOverDate==0">
        and date_format(now(), '%y-%m-%d') < t1.supp_date
    </if>
    order by t1.create_time
    limit #{page.limitIndex}, #{page.pageSize}
</select>
1.4.2 Service
    @Autowired
    private ProductMapper productMapper;
    @Override
    public Page queryProductPage(Page page, Product product) {
//      查询商品行数
        Integer count = productMapper.findProductRowCount(product);
        page.setTotalNum(count);
        page.setLimitIndex(page.getLimitIndex());
        page.setPageCount(page.getPageCount());
//      分页查询商品
        List<Product> productPage = productMapper.findProductPage(page, product);
        page.setResultList(productPage);
        return page;
    }
1.4.3 Controller
    //分页查询商品的url接口
    @RequestMapping("/product-page-list")
    public Result productListPage(Page page, Product product) {
        page = productService.queryProductPage(page, product);
        return Result.ok(page);
    }
1.4.4 效果图

二、添加商品

2.1 查询所有分类树
2.1.1 Mapper
//  查询所有商品分类的方法
    public List<ProductType>  findAllProductType();
<select id="findAllProductType" resultType="com.pn.entity.ProductType">
    select *
    from product_type
</select>
2.1.2 Service
@CacheConfig(cacheNames = "com.pn.service.impl.ProductTypeServiceImpl")
@Service
public class ProductTypeServiceImpl implements ProductTypeService {
    @Autowired
    private ProductMapper productMapper;
    @Cacheable(key = "'all:typeTree'")
    @Override
    public List<ProductType> productTypeTree() {
//      查询出所有的商品分类
        List<ProductType> allProductType = productMapper.findAllProductType();
//      将所有商品分类转换成商品分类树
        return allTypeToTypeTree(allProductType,0);
    }
    private List<ProductType> allTypeToTypeTree(List<ProductType> typeList, Integer pid) {
//      拿到n级分类
        List<ProductType> nTypeList = new ArrayList<>();
        for (ProductType productType : typeList) {
            if (productType.getParentId().equals(pid)) {
                nTypeList.add(productType);
            }
        }
//      遍历n级分类,为其找子
        for (ProductType productType : nTypeList) {
//          找子集
            List<ProductType> productTypes = allTypeToTypeTree(typeList, productType.getTypeId());
            productType.setChildProductCategory(productTypes);
        }
        return nTypeList;
    }
}
2.1.3 Controller
@Autowired
private ProductTypeService productTypeTree;
//查询所有商品分类树的接口
@RequestMapping("/category-tree")
public Result loadTypeTree(){
    return Result.ok(productTypeTree.productTypeTree());
}
2.1.4 效果图

2.2 查询所有供应商
2.2.1 Mapper
//  查询所有供应商的方法
    public List<Supply> findAllSupply();
<select id="findAllSupply" resultType="com.pn.entity.Supply">
    select *
    from supply
    where is_delete = 0
</select>
2.2.2 Service
@CacheConfig(cacheNames = "com.pn.service.impl.SupplyServiceImpl")
@Service
public class SupplyServiceImpl implements SupplyService {
    @Autowired
    private SupplyMapper supplyMapper;
    @Cacheable(key = "'all:supply'")
    @Override
    public List<Supply> supplyMapper() {
        return supplyMapper.findAllSupply();
    }
}
2.2.3 Controller
@Autowired
private SupplyService supplyService;
// 查询所有供应商的接口
@RequestMapping("/supply-list")
public Result supplyList(){
    return Result.ok(supplyService.supplyMapper());
}
2.2.4 效果图

2.3 查询所有产地
2.3.1 Mapper
//查询所有产地
public List<Place> findAllPlace();
<select id="findAllPlace" resultType="com.pn.entity.Place">
    select * from place
</select>
2.3.2 Service
//指定缓存的名称即键的前缀,一般是@CacheConfig标注的类的全类名
@CacheConfig(cacheNames = "com.pn.service.impl.PlaceServiceImpl")
@Service
public class PlaceServiceImpl implements PlaceService {
    //注入PlaceMapper
    @Autowired
    private PlaceMapper placeMapper;
    /*
      查询所有产地的业务方法
     */
    //对查询到的所有产地进行缓存,缓存到redis的键为all:place
    @Cacheable(key = "'all:place'")
    @Override
    public List<Place> queryAllPlace() {
        //查询所有产地
        return placeMapper.findAllPlace();
    }
}
2.3.3 Controller
    @RequestMapping("/place-list")
    public Result placeList(){
//      执行业务
        return Result.ok(placeService.queryAllPlace());
    }
2.3.4 效果图

2.4 查询所有单位
2.4.1 Mapper
//查询所有单位的方法
public List<Unit> findAllUnit();
<select id="findAllUnit" resultType="com.pn.entity.Unit">
    select * from unit
</select>
2.4.2 Service
//指定缓存的名称即键的前缀,一般是@CacheConfig标注的类的全类名
@CacheConfig(cacheNames = "com.pn.service.impl.UnitServiceImpl")
@Service
public class UnitServiceImpl implements UnitService {
    //注入UnitMapper
    @Autowired
    private UnitMapper unitMapper;
    /*
      查询所有单位的业务方法
     */
    //对查询到的所有单位进行缓存,缓存到redis的键为all:unit
    @Cacheable(key = "'all:unit'")
    @Override
    public List<Unit> queryAllUnit() {
        //查询所有单位
        return unitMapper.findAllUnit();
    }
}
2.4.3 Controller
    @RequestMapping("/unit-list")
    public Result unitList(){
//      执行业务
        return Result.ok(unitService.queryAllUnit());
    }
2.4.4 效果图

2.5 上传图片
我们尝试一下上传图片,发现请求负载是一个上传的图片的二进制

之后我们把图片上传到下图所示位置,但是这是未编译的资源

我们真正上传到服务器上的项目是编译后的项目

简单来说我们图片的上传位置并不是未编译之前的位置,而是编译之后且部署到服务器的classes/static/img/upload位置
所以我们在配置文件中就可以配置
#自定义属性 - 图片上传到的位置
file:
  upload-path: "classpath:static/img/upload"
  #自定义属性 - 上传的图片保存数据库的访问路径的目录路径
  access-path: "/img/upload/"
这个upload-path可以理解为未编译时,存放到上面的图2,编译后存放到上图的图3
2.5.1 Mapper
2.5.2 Service
2.5.3 Controller
 //   上传图片url接口
    //    file.transferTo(上传的文件保存到的磁盘文件的File对象)); --实现文件的上传
    @RequestMapping("/img-upload")
    @CrossOrigin//表示此接口允许被跨域请求
    public Result uploadImage(MultipartFile file) throws IOException {
        log.info("uploadAddress - "+uploadAddress);
//      实现文件的上传
//      拿到图片上传到的目录路径的file对象 - classpath:static/img/upload 图片要上传到的路径
        File uploadDirFile = ResourceUtils.getFile(uploadAddress);
//        String file1 = Objects.requireNonNull(this.getClass().getClassLoader().getResource("static/image/upload/")).getFile();
//        System.out.println(file1);
//      我们之前封装目录路径的方式如下所示
//      File f = new File(uploadAddress);
//      现在我们并不能这么封装,因为这个路径是特殊的类路径的路径,我们需要一个特定的工具类进行解析
//      拿到图片上传到的目录路径的磁盘路径
        String uploadDirPath = uploadDirFile.getAbsolutePath();
        log.info("uploadDirPath - "+uploadDirPath);
//      拿到我们上传的图片的名称
        String originalFilename = file.getOriginalFilename();
//      拿到上传的文件要保存到的磁盘文件的路径
        String uploadFilePath = uploadDirPath + "/" + originalFilename;
//      File对象表示文件保存到的那个位置
        file.transferTo(new File(uploadFilePath));
        return Result.ok("图片上传成功!");
    }
2.5.4 效果图
图片上传到下面的位置


2.6 添加商品
/product/product-add 真正添加商品的请求

2.6.1 Mapper
备注:商品名可以重复,但是型号不能重复
//  添加商品的方法
    public int insertProduct(Product product);
<!--添加商品的方法-->
<insert id="insertProduct">
    insert into product
    values (null, #{storeId}, #{brandId}, #{productName}, #{productNum},
            #{productInvent}, #{typeId}, #{supplyId}, #{placeId}, #{unitId},
            #{introduce}, 0, #{inPrice}, #{salePrice}, #{memPrice}, now(),
            null, #{createBy}, null, #{imgs}, #{productDate}, #{suppDate})
</insert>
//  根据型号查询商品的方法
    public Product findProductByNum(@Param("productNum") String productNum);
<select id="findProductByNum" resultType="com.pn.entity.Product">
    select  * from product where product_num=#{productNum}
</select>
2.6.2 Service
//  添加商品的业务方法
    @Override
    public Result addProduct(Product product) {
//      先判断商品的型号是否已存在
        Product prct = productMapper.findProductByNum(product.getProductNum());
        if (prct != null){
            return Result.err(501,"商品型号已经存在");
        }
//      设定图片的访问地址(product.getImgs()前端传输过来的只有图片的名字)
        product.setImgs(imageURL+product.getImgs());
//      添加商品
        int success = productMapper.insertProduct(product);
        if(success>0){
            return Result.ok("添加商品成功");
        }
        return Result.err(501,"添加商品失败");
    }
2.6.3 Controller
    @Autowired
    private TokenUtils tokenUtils;
//   添加商品的URL接口
    @RequestMapping("/product-add")
    public Result addProduct(@RequestBody Product product, @RequestHeader("Token")String token){
//      拿到当前登录的用户id
        CurrentUser currentUser = tokenUtils.getCurrentUser(token);
        
        product.setCreateBy(currentUser.getUserId());
        
        return productService.addProduct(product);
    }
2.6.4 效果图


三、修改商品
3.1 上下架状态
3.1.1 Mapper
//  根据商品id修改商品上下架状态的方法
    public int setStateByPid(@Param("productId")Integer productId,@Param("upDownState")String upDownState);
<update id="setStateByPid">
    update product set up_down_state=#{upDownState} where product_id = #{productId}
</update>
3.1.2 Service
@Override
public Result updateStateByPid(Product product) {
    int success = productMapper.setStateByPid(product.getProductId(), product.getUpDownState());
    return success>0 ? Result.ok("商品上下架状态修改成功"):Result.err(501,"商品上下架状态修改失败");
}
3.1.3 Controller
//  修改商品上下架状态
@RequestMapping("/state-change")
public Result changeProductState(@RequestBody Product product) {
    return productService.updateStateByPid(product);
}
3.2 修改商品
当我们修改商品信息的时候并没有修改图片,如下所示
图片没有被修改的时候还是完整的访问地址

当我们修改商品的信息的时候修改了图片,如下所示
我们imgs的内容变成了图片的名称

3.2.1 Mapper
根据商品的id修改商品信息
//  根据商品的id修改商品的信息
    public int setProductById(Product product);
3.2.2 Service
    @Value("${file.access-path}")
    private String imageURL; //   /img/upload/
    @Override
    public Result setProductById(Product product) {
//      首先看一下型号(根据型号获取product对象)
        Product prod = productMapper.findProductByNum(product.getProductNum());
//      判断型号是否改动
        if (prod != null && !prod.getProductId().equals(product.getProductId())) {
//          表示商品的型号被修改了,并且修改后的型号已经存在
            return Result.err(501, "修改后的型号已经存在");
        }
//      处理突图片,判断图片是否被修改(如果图片被修改了,则这个参数是一个图片的名称,反之是一个完整的文件路径)
        String imgs = product.getImgs();
        if (!imgs.contains(imageURL)){
//          说明图片被修改过了,product.getImgs()的值只是图片的名称
            product.setImgs(imageURL+product.getImgs());
        }
//      修改商品信息
        int success = productMapper.setProductById(product);
        return success > 0 ? Result.ok("商品修改成功") : Result.err(501, "商品修改失败");
    }
3.2.3 Controller
//   修改商品信息
    @RequestMapping("/product-update")
    public Result updateProduct(@RequestBody Product product ,@RequestHeader("Token") String token){
        //拿到当前登录的用户id
        CurrentUser currentUser = tokenUtils.getCurrentUser(token);
        product.setCreateBy(currentUser.getUserId());
        return productService.setProductById(product);
    }
四、删除商品
4.1 Mapper
//  根据商品id删除商品的方法
    public int removeProductByIds(List<Integer> productIdList);
<delete id="removeProductByIds">
    delete from product
    where product_id in
    <foreach collection="list" open="(" close=")" separator="," item="prod">
        #{prod}
    </foreach>
</delete>
4.2 Service
@Override
public Result deleteProductByIds(List<Integer> productIdList) {
    int success = productMapper.removeProductByIds(productIdList);
    return success>0 ? Result.ok("商品删除成功"):Result.err(501,"商品删除失败");
}
4.3 controller
    //  删除单个商品
    @RequestMapping("/product-delete/{productId}")
    public Result deleteProduct(@PathVariable Integer productId) {
        return productService.deleteProductByIds(Arrays.asList(productId));
    }
    //  批量删除商品
    @RequestMapping("/product-delete")
    public Result deleteProductList(@PathVariable List<Integer> productIdList) {
        return productService.deleteProductByIds(productIdList);
    }
五、添加采购单(核心)
5.1 采购流程
类似进货
采购流程
-  在商品列表针对具体的商品添加采购单  向buy_list表中添加一条记录。添加的采购单表示预买的商品(还没有买,is_in字段的值是0)  
-  当商品采购到之后进行入库 在采购单列表做入库操作,向in_store表添加记录(状态是0未入库),同时修改采购单表buy_list表由0改为1入库 

 下面是in_store表

-  商品真正的入库 在入库单列表做确认入库操作  将入库单表in_store表的入库单状态由0改为1入库 
5.2 采购单实体类
/**
 * 采购单表buy_list表的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Purchase {
    private Integer buyId;//采购单id
    private Integer productId;//采购单采购的商品id
    private Integer storeId;//采购单采购的商品所在仓库id
    private Integer buyNum;//预计采购的商品数量
    private Integer factBuyNum;//实际采购的商品数量
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date buyTime;//采购时间
    private Integer supplyId;//采购单采购的商品的供应商id
    private Integer placeId;//采购单采购的商品的产地id
    private String buyUser;//采购人
    private String phone;//采购人联系电话
    private String isIn;//是否生成入库单,1.是,0.否
    //---------------追加属性---------------------------
    private String productName;//商品名称
    private String storeName;//仓库名称
    private String startTime;//搜索起始时间
    private String endTime;//搜索结束时间
}
5.3 添加采购单

采购单数据库表buy_list如下所示


5.3.1 Mapper
@Mapper
public interface PurchaseMapper {
//   添加采购单
    public int insertPurchase(Purchase purchase);
    
}
<insert id="insertPurchase">
    insert into buy_list
    values (null, #{productId}, #{storeId}, #{buyNum}, null, now(),
            #{supplyId}, #{placeId}, #{buyUser}, #{phone}, 0)
</insert>
5.3.2 Service
@Service
public class PurchaseServiceImpl implements PurchaseService {
    @Autowired
    private PurchaseMapper purchaseMapper;
    @Override
    public Result savePurchase(Purchase purchase) {
//      初始化时,实际采购数量要和预计采购数量一致
        purchase.setFactBuyNum(purchase.getBuyNum());
        int success = purchaseMapper.insertPurchase(purchase);
        return success>0 ? Result.ok("添加采购单成功") : Result.err(501,"添加采购单失败");
    }
}
5.3.3 Controller
@RestController
@RequestMapping("/purchase")
public class PurchaseController {
    @Autowired
    private PurchaseService purchaseService;
    @RequestMapping("/purchase-add")
    public Result addPurchase(@RequestBody Purchase purchase){
        return purchaseService.savePurchase(purchase);
    }
}
5.3.4 效果图


六、添加出库单(核心)
6.1 出库流程
-  在商品列表针对具体的商品添加出库单 向出库单表out_store表添加出库单(状态是0,未出库),准备出库   
-  商品真正出库之后,在出库单列表做确认出库操作 商品真正出库之后,在出库单列表做确认出库操作 将出库单表out_store表的出库状态由0改为1表示已出库 

5.2 出库单实体类


/**
 * 出库单表out_store表的实体类:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class OutStore {
    private Integer outsId;//出库单id
    private Integer productId;//出库单出库的商品id
    private Integer storeId;//出库单出库的商品所在仓库id
    private Integer tallyId;//理货员id
    private Double outPrice;//商品的出库价格
    private Integer outNum;//出库的商品数量
    private Integer createBy;//创建出库单的用户id
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;//创建出库单的时间
    private String isOut;//是否出库,0.未出库,1.已出库
    //------------------追加的属性-------------------------
    private String productName;//商品名称
    private String startTime;//起始时间
    private String endTime;//结束时间
    private String storeName;//仓库名称
    private String userCode;//创建出库单的用户的名称
}
5.3 添加出库单
需要先上架,才能进行“出库”

5.3.1 Mapper
//添加出库单的方法
public int insertOutStore(OutStore outStore);
<insert id="insertOutStore">
    insert into out_store values(
      null, #{productId}, #{storeId}, null, null,
      #{outNum}, #{createBy}, now(), 0
    )
</insert>
5.3.2 Service
@Service
public class OutStoreServiceImpl implements OutStoreService {
  @Autowired
  private OutStoreMapper outStoreMapper;
    @Override
    public Result saveOutStore(OutStore outStore) {
//      添加
        int success = outStoreMapper.insertOutStore(outStore);
        return success>0? Result.ok("添加出货单成功") :Result.err(501,"添加出货单失败");
    }
}
5.3.3 Controller
@RequestMapping("/outstore")
@RestController
public class OutStoreController {
    //注入OutStoreService
    @Autowired
    private OutStoreService outStoreService;
    //注入TokenUtils
    @Autowired
    private TokenUtils tokenUtils;
    @RequestMapping("/outstore-add")
    public Result addOutStore(@RequestBody OutStore outStore,
                              @RequestHeader("Token") String token){
        //获取当前登录的用户
        CurrentUser currentUser = tokenUtils.getCurrentUser(token);
        //获取当前登录的用户id,即添加出库单的用户id
        int createBy = currentUser.getUserId();
        outStore.setCreateBy(createBy);
        //响应
        return outStoreService.saveOutStore(outStore);
    }
}
5.3.4 效果图


七、商品分类
7.1 查询商品分类树
这个地方在2.1写过,我们重新写一个接口即可
7.1.1 Controller
//  查询商品分类树的url
    @RequestMapping("/product-category-tree")
    public Result productCategoryTree(){
//      执行业务
     return  Result.ok(productTypeService.productTypeTree())  ;
    }
7.1.2 效果图

7.2 添加商品分类
一种是添加一级分类,直接添加即可

一种是添加二级分类,需要先勾选一级分类

分类编码type_code字段不能重复,这个地方的校验时当光标离开“分类编码”文本框的时候便开始校验
分类名type_name字段不能重复

7.2.1 Mapper
校验分类编码、分类名称是否重复的Mapper方法
//  根据分类编码或者分类名称查询商品分类的方法
    public ProductType findTypeByCodeOrName(ProductType productType);
    <select id="findTypeByCodeOrName" resultType="com.pn.entity.ProductType">
        select *
        from product_type
        <where>
            <if test="typeName !=null and typeName != '' ">
                type_name = #{typeName}
            </if>
            <if test="typeCode !=null and typeCode != '' ">
               or type_code = #{typeCode}
            </if>
        </where>
    </select>
添加商品分类的Mapper方法
//  添加商品分类的方法
    public int insertProductType(ProductType productType);
<insert id="insertProductType">
    insert into product_type
    values (null, #{parentId}, #{typeCode}, #{typeName}, #{typeDesc})
</insert>
7.2.2 Service
校验分类编码、校验分类名称的业务方法分开,因为校验分类编码是否重复是在光标离开文本框的时候发送请求进行校验的,但是校验分类名称的时候是在提交表单数据的时候
校验分类编码是否重复的方法
    @Override
    public Result checkTypeCode(String typeCode) {
//      根据分类编码查询分类,并判断是否存在
        ProductType productType  = new ProductType();
        productType.setTypeCode(typeCode);
        ProductType prodType = productMapper.findTypeByCodeOrName(productType);
        return Result.ok(prodType == null);
    }
添加商品分类
    @CacheEvict(key = "'all:typeTree'")
    @Override
    public Result saveProductType(ProductType productType) {
//      首先校验一下分类名称是否存在
        ProductType prodType = new ProductType();
        prodType.setTypeName(productType.getTypeName());
        ProductType prodCategory = productMapper.findTypeByCodeOrName(prodType);
        if (prodCategory!=null){
            return Result.err(501,"分类名称已经存在");
        }
//      分类名称不存在,添加分类
        int success = productMapper.insertProductType(productType);
        return success>0 ? Result.ok("添加分类成功") : Result.err(501,"添加分类失败");
    }
7.2.3 Controller
校验分类编码是否重复的Controller
//  校验分类编码是否已经存在
    @RequestMapping("/verify-type-code")
    public Result checkTypeCode(String typeCode){
//      执行业务
       return productTypeService.checkTypeCode(typeCode);
    }
添加商品分类Controller
//  添加商品分类的url接口
    @RequestMapping("/type-add")
    public Result addProductType(@RequestBody ProductType productType){
        return productTypeService.saveProductType(productType);
7.2.4 效果图
当“分类编码”文本框失去焦点后如下所示

添加商品分类


7.3 删除商品分类
删除商品分类,我们要删除哪个商品分类,就勾选哪个商品分类
如果删除父级分类的话,子级分类也要删除
根据分类id或父级分类id删除分类
7.3.1 Mapper
//  根据分类id或父级分类id删除分类的方法
    public int removeProductType(Integer typeId);
<delete id="removeProductType">
    delete
    from product_type
    where type_id = #{typeId} or parent_id = #{typeId}
</delete>
7.3.2 Service
    @CacheEvict(key = "'all:typeTree'")
    @Override
    public Result deleteProductType(Integer typeId) {
        int success = productMapper.removeProductType(typeId);
        return success>0? Result.ok("删除成功") : Result.err(501,"删除失败");
    }
7.3.3 Controller
//  删除商品分类
    @RequestMapping("/type-delete/{typeId}")
    public Result typeDelete(@PathVariable Integer typeId){
       return productTypeService.deleteProductType(typeId);
    }
7.4 修改商品分类
只能改分类名称和分类描述


7.4.1 Mapper
//  根据分类id修改分类的方法
    public int setProductTypeById(ProductType productType);
<update id="setProductTypeById">
    update product_type
    set type_name=#{typeName},
        type_desc=#{typeDesc}
    where type_id= #{typeId}
</update>
7.4.2 Service
    @CacheEvict(key = "'all:typeTree'")
    @Override
    public Result setProductType(ProductType productType) {
//      修改的分类名称是否已经存在
        ProductType prodType = new ProductType();
        prodType.setTypeName(productType.getTypeName());
        ProductType prodCategory = productMapper.findTypeByCodeOrName(prodType);
        if (prodCategory!=null && !prodCategory.getTypeId().equals((productType.getTypeId()))){
            return Result.err(501,"分类名称已经存在");
        }
        int success = productMapper.setProductTypeById(productType);
        return success>0? Result.ok("修改分类成功") : Result.err(501,"修改分类失败");
    }
7.4.3 Controller
//  修改商品分类的url
    @RequestMapping("/type-update")
    public Result updateProductType(@RequestBody ProductType productType){
        return productTypeService.setProductType(productType);
    }
7.4.4 效果图







![[CISCN2019 华东南赛区]Web11 SSTI](https://img-blog.csdnimg.cn/48d65210517a461ea2ade83c5b3478b4.png)
![buuctf-[WUSTCTF2020]朴实无华](https://img-blog.csdnimg.cn/f594924cce334e57966cae193c136289.png)













