乐优商城学习Day04:
注意:此次代码都是在第三天的基础上
第三天的链接如下:
https://blog.csdn.net/zcylxzyh/article/details/98210600
此次笔记内容主要为:
1.把图片放入虚拟机
2.查询规格组和查询规格参数的实现
3.实现SPU
下面开始第四天的学习:
1.把图片放进虚拟机:
创建static文件夹
进入static,把图片放入
然后去命令台解压:
解压
然后删除安装包:
进入images
修改nginx配置:
至此结束。
2.查询规格组和查询规格参数的实现
先写实体类:
在ly-item-interface中如下创建实体类SpecGroup(规格组的实体类),SpecParam(规格参数的实体类)
SpecGroup代码如下:
package com.leyou.item.pojo;
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Table(name = "tb_spec_group")
public class SpecGroup {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private Long cid;
private String name;
}
SpecParam代码如下:
package com.leyou.item.pojo;
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "tb_spec_param")
@Data
public class SpecParam {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private Long cid;
private Long groupId;
private String name;
@Column(name = "`numeric`")
private Boolean numeric;
private String unit;
private Boolean generic;
private Boolean searching;
private String segments;
}
然后在写mapper
在ly-item-service中如下位置创建SpecGroupMapper,SpecParamMapper 接口
SpecGroupMapper代码如下:
package com.leyou.item.mapper;
import com.leyou.item.pojo.SpecGroup;
import tk.mybatis.mapper.common.Mapper;
public interface SpecGroupMapper extends Mapper<SpecGroup>{
}
SpecParamMapper 代码如下
package com.leyou.item.mapper;
import com.leyou.item.pojo.SpecParam;
import tk.mybatis.mapper.common.Mapper;
public interface SpecParamMapper extends Mapper<SpecParam> {
}
在写controller和service
在这里,我们把查询规格参数和查询规格组的功能实现放到一个controller、service中:
SpecificationService代码如下:
package com.leyou.item.service;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.item.mapper.SpecGroupMapper;
import com.leyou.item.mapper.SpecParamMapper;
import com.leyou.item.pojo.SpecGroup;
import com.leyou.item.pojo.SpecParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class SpecificationService {
@Autowired
private SpecGroupMapper groupMapper;
@Autowired
private SpecParamMapper paramMapper;
public List<SpecGroup> queryGroupByCid(Long cid) {
//查询条件
SpecGroup group = new SpecGroup();
group.setCid(cid);
//开始查询
List<SpecGroup> list = groupMapper.select(group);
if(CollectionUtils.isEmpty(list)){
//没查到
throw new LyException(ExceptionEnum.SPEC_GROUP_NOT_FOUND);
}
return list;
}
public List<SpecParam> queryParamByGid(Long gid) {
SpecParam param = new SpecParam();
param.setGroupId(gid);
List<SpecParam> list = paramMapper.select(param);
if(CollectionUtils.isEmpty(list)){
//没查到
throw new LyException(ExceptionEnum.SPEC_PARAM_NOT_FOUND);
}
return list;
}
}
SpecificationController代码如下:
package com.leyou.item.web;
import com.leyou.item.pojo.SpecGroup;
import com.leyou.item.pojo.SpecParam;
import com.leyou.item.service.SpecificationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("spec")
public class SpecificationController {
@Autowired
private SpecificationService specService;
/*
* 根据分类id查询规格组
*
* */
@GetMapping("groups/{cid}")
public ResponseEntity<List<SpecGroup>> queryGroupByCid(@PathVariable("cid")Long cid){
return ResponseEntity.ok(specService.queryGroupByCid(cid));
}
//根据组id查询参数
@GetMapping("params")
public ResponseEntity<List<SpecParam>> queryParamByGid(@RequestParam("gid")Long gid){
return ResponseEntity.ok(specService.queryParamByGid(gid));
}
}
结果:
规格组:
规格参数:
至此,第二部分结束。
3.实现SPU
SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集
SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品
- 本页的 华为Mate10 就是一个商品集(SPU)
- 因为颜色、内存等不同,而细分出不同的Mate10,如亮黑色128G版。(SKU)
可以看出:
- SPU是一个抽象的商品集概念,为了方便后台的管理。
- SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样,用户购买的是SKU而不是SPU
下面实现SPU:
首先引入SPU的两个实体类Spu,SpuDetail(Spu的详情),我们做了表的垂直拆分,将SPU的详情放到了另一张表:tb_spu_detail因此有两个实体类:
Spu代码如下:
package com.leyou.item.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.Date;
@Table(name = "tb_spu")
@Data
public class Spu {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private Long brandId;
private Long cid1;//1级类目
private Long cid2;//2级类目
private Long cid3;//3级类目
private String title;//标题
private String subTitle;//子标题
private Boolean saleable;//是否上架
@JsonIgnore
private Boolean valid;//是否有效,逻辑删除用
private Date createTime;//创建时间
@JsonIgnore //返回到页面时忽略这个字段
private Date lastUpdateTime;//最后修改时间
@Transient //因为不是数据库字段所以加上这个注解,不是bean包下的
private String cname;
@Transient
private String bname;
}
SpuDetail代码如下:
package com.leyou.item.pojo;
import lombok.Data;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "tb_spu_detail")
@Data
public class SpuDetail {
@Id
private Long spuId;
private String description;
private String specialSpec;
private String genericSpec;
private String packingList;
private String afterService;
}
写SpuMapper和SpuDetailMapper
SpuMapper代码如下:
package com.leyou.item.mapper;
import com.leyou.item.pojo.Spu;
import tk.mybatis.mapper.common.Mapper;
public interface SpuMapper extends Mapper<Spu> {
}
SpuDetailMapper代码如下:
package com.leyou.item.mapper;
import com.leyou.item.pojo.SpuDetail;
import tk.mybatis.mapper.common.Mapper;
public interface SpuDetailMapper extends Mapper<SpuDetail> {
}
然后写Service,这里不管是SPU,SKU(明天学习)还是SPUDetail…都用一个Service:GoodsService
GoodsService代码如下:
package com.leyou.item.service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.vo.PageResult;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.pojo.Category;
import com.leyou.item.pojo.Spu;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class GoodsService {
@Autowired
private SpuMapper spuMapper;
@Autowired
private SpuDetailMapper detailMapper;
@Autowired CategoryService categoryService;
@Autowired BrandService brandService;
public PageResult<Spu> querySpuByPage(Integer page, Integer rows, Boolean saleable, String key) {
//分页
PageHelper.startPage(page,rows);
//过滤
Example example = new Example(Spu.class);
//搜索字段过滤
Example.Criteria criteria = example.createCriteria();
if(StringUtils.isNotBlank(key)){
criteria.andLike("title","%"+key+"%");
}
//上下架过滤
if (saleable != null){
criteria.andEqualTo("saleable",saleable);
}
//默认排序
example.setOrderByClause("last_update_time DESC");
//查询
List<Spu> spus = spuMapper.selectByExample(example);
//判断
if(CollectionUtils.isEmpty(spus)){
throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
}
//解析分类和品牌的名称
loadCategoryAndBrandName(spus);
//解析分页结果
PageInfo<Spu> info = new PageInfo<>(spus);
return new PageResult<>(info.getTotal(),spus);
}
private void loadCategoryAndBrandName(List<Spu> spus) {
for(Spu spu : spus){
//处理分类名称
List<String> names = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()))
.stream().map(Category::getName).collect(Collectors.toList());
spu.setCname(StringUtils.join(names,"/"));
//处理品牌名称
spu.setBname(brandService.queryById(spu.getBrandId()).getName());
}
}
}
然后写Controller,这里不管是SPU,SKU(明天学习)还是SPUDetail…都用一个Controller:GoodsController
GoodsController代码如下:
package com.leyou.item.web;
import com.leyou.common.vo.PageResult;
import com.leyou.item.pojo.Spu;
import com.leyou.item.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("/spu/page")
public ResponseEntity<PageResult<Spu>> querySpuByPage(
@RequestParam(value = "page",defaultValue = "1") Integer page,
@RequestParam(value = "rows",defaultValue = "5") Integer rows,
@RequestParam(value = "saleable",required = false) Boolean saleable,
@RequestParam(value = "key",required = false) String key
){
return ResponseEntity.ok(goodsService.querySpuByPage(page,rows,saleable,key));
}
}
今天的代码实现了新的功能,需要修改Category的Service和Mapper、Brand的Service和Mapper,枚举类,ly-item-interface的pom.xml:
Category的Service和Mapper:
Mapper
package com.leyou.item.mapper;
import com.leyou.item.pojo.Category;
import tk.mybatis.mapper.additional.idlist.IdListMapper;
import tk.mybatis.mapper.common.Mapper;
public interface CategoryMapper extends Mapper<Category>,IdListMapper<Category,Long>{
}
Service
package com.leyou.item.service;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.item.pojo.Category;
import com.leyou.item.mapper.CategoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class CategoryService {
@Autowired
private CategoryMapper categoryMapper;
public List<Category> queryCategoryListByPid(Long pid) {
//查询条件,mapper会把对象中的非空属性作为查询条件
Category t = new Category();
t.setParentId(pid);
List<Category> list = categoryMapper.select(t);
//判断结果
if (CollectionUtils.isEmpty(list)) {
//返回404
throw new LyException(ExceptionEnum.CATEGORY_NOT_FOND);
}
return list;
}
public List<Category> queryByIds(List<Long> ids){
List<Category> list = categoryMapper.selectByIdList(ids);
if (CollectionUtils.isEmpty(list)) {
//返回404
throw new LyException(ExceptionEnum.CATEGORY_NOT_FOND);
}
return list;
}
}
Brand的Service和Mapper
Mapper
package com.leyou.item.mapper;
import com.leyou.item.pojo.Brand;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import tk.mybatis.mapper.common.Mapper;
public interface BrandMapper extends Mapper<Brand> {
@Insert("INSERT INTO tb_category_brand (category_id,brand_id) VALUES (#{cid},#{bid})")
int insertCategoryBrand(@Param("cid") Long cid ,@Param("bid") Long bid);
}
Service
package com.leyou.item.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exception.LyException;
import com.leyou.common.vo.PageResult;
import com.leyou.item.mapper.BrandMapper;
import com.leyou.item.pojo.Brand;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;
import java.util.List;
@Service
public class BrandService {
@Autowired
private BrandMapper brandMapper;
//sortBy 根据什么排序,比如id
public PageResult<Brand> queryBrandByPage(Integer page, Integer rows, String sortBy, Boolean desc, String key) {
//分页,使用分页助手,在查询语句前调用这个方法就能实现分页,这里是page,rows
PageHelper.startPage(page,rows);//第一个参数:当前页,第二个参数:每页大小
//Example需要一个实体类(字节码),让他知道去哪个表查
Example example = new Example(Brand.class);
//条件过滤,查询用的,这里是key
if (StringUtils.isNotBlank(key)){
//过滤条件,也就是sql语句
/*
* WHERE 'name' LIKE "%X%" OR letter =='X'
* ORDER BY id DESC
* */
//key.toUpperCase()转大写
example.createCriteria().orLike("name","%"+key+"%")
.orEqualTo("letter",key.toUpperCase());
}
//排序
if(StringUtils.isNotBlank(sortBy)){
String orderByClause = sortBy+ (desc ? "DESC":"ASC");
example.setOrderByClause(orderByClause);
}
//查询
List<Brand> list = brandMapper.selectByExample(example);
//如果为空,证明没查到
if(CollectionUtils.isEmpty(list)){
throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);
}
//解析分页结果
PageInfo<Brand> info = new PageInfo<>(list);
//返回总条数和list
return new PageResult<>(info.getTotal(),list);
}
//新增,因为复杂需要加上事务注解
@Transactional
public void saveBrand(Brand brand, List<Long> cids) {
//新增品牌
brand.setId(null);
int count = brandMapper.insert(brand);
if(count!=1){
throw new LyException(ExceptionEnum.BRAND_SAVE_ERROR);
}
//新增中间表
for (Long cid : cids) {
count=brandMapper.insertCategoryBrand(cid,brand.getId());
if(count != 1){
throw new LyException(ExceptionEnum.BRAND_SAVE_ERROR);
}
}
}
public Brand queryById(Long id){
Brand brand = brandMapper.selectByPrimaryKey(id);
if(brand ==null){
throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);
}
return brand;
}
}
枚举类:
package com.leyou.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
//枚举是指只能有固定实例个数的类
public enum ExceptionEnum {
PRICE_CANNOT_BE_NULL(400,"价格不能为空!"),
CATEGORY_NOT_FOND(404,"商品分类没查到"),
BRAND_NOT_FOUND(404,"品牌不存在"),
BRAND_SAVE_ERROR(500,"新增品牌失败"),
UPLOAD_FILE_ERROR(500,"文件上传失败"),
INVALID_FILE_TYPE(400,"无效的文件类型"),
SPEC_GROUP_NOT_FOUND(404,"商品规格组不存在"),
SPEC_PARAM_NOT_FOUND(404,"商品规格参数不存在"),
GOODS_NOT_FOUND(404,"商品不存在"),
;
private int code ;
private String msg ;
}
ly-item-interface的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ly-item</artifactId>
<groupId>com.leyou.service</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.leyou.service</groupId>
<artifactId>ly-item-interface</artifactId>
<dependencies>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-core</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
</dependencies>
</project>
结果:
至此,第三部分结束。
修改后的nginx配置:
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 10m;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 9001;
server_name manage.leyou.com;
location / {
proxy_pass http://192.168.31.193:9001;
}
}
server {
listen 80;
server_name manage.leyou.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://192.168.31.193:9001;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
server {
listen 80;
server_name api.leyou.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /api/upload {
rewrite "^/(.*)$" /zuul/$1;
}
location / {
proxy_pass http://192.168.31.193:10010;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
server {
listen 80;
server_name image.leyou.com;
# 监听域名中带有group的,交给FastDFS模块处理
location ~/group([0-9])/ {
ngx_fastdfs_module;
}
location / {
root /leyou/static/;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
至此今天的学习结束。