项目2个人接口开发指导 ​
该文档用来指导大家开发个人接口
依赖 ​
整合web、整合mybatis、整合pagehelper、整合shiro
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>通用部分 ​
跨域 ​
跨域问题,需要在响应报文中增加一些响应头,这个是通用的部分,这时候我们使用CorsFilter来完成
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}// 已经不使用了
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedOrigins("*")
.allowedMethods("*");
}整合MyBatis ​
引入mybatis-spring-boot-starter依赖,然后配置spring-datasource的相关配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/mall40?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
type: druid的全限定类名
mybatis:
type-handlers-package: typehandler的包目录然后在启动类上增加Mapper的包目录配置
@MapperScan("com.cskaoyan.mapper")
@SpringBootApplication
public class MarketApplication {
public static void main(String[] args) {
SpringApplication.run(MarketApplication.class, args);
}
}个人信息 ​
需要整合Shiro之后,通过Shiro能够拿到信息
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
Object primaryPrincipal = subject.getPrincipals().getPrimaryPrincipal();
//Object principal = subject.getPrincipal();
}json格式转换 ​
可以使用SpringBoot的配置文件配置其格式信息
spring:
jackson:
date-format: yyyy-MM-dd
time-zone: GMT+8@JsonFormat写在成员变量上,指定该成员变量的日期格式
- 接收JSON字符串,@RequestBody接收为一个引用类型对象 ,引用类型对象中有Date成员变量
- 响应JSON字符串,@ResponseBody响应引用类型对象 ,引用类型对象中有Date成员变量
分页配置 ​
引入pagehelper的starter之后,引入对应的dialect即可
pagehelper:
helper-dialect: mysql配置文件 ​
spring.profiles.active
# application.yml → 主配置文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/cskaoyan_market?useUnicode=true&characterEncoding=utf-8
profiles:
active: root,web比如数据用户名密码不一致的情况会导致冲突
不同的开发者,写不同的配置文件
zhangsan的配置文件
# application-zhangsan.yml
spring:
datasource:
username: zhangsan
password: lovexiaofanglisi的配置文件
# application-lisi.yml
spring:
datasource:
username: lisi
password: 654321root用户的配置文件
# application-root.yml
spring:
datasource:
username: root
password: 123456spring.profiles.active里的值不还是会冲突么?
.gitignore 把application.yml给忽略掉
HibernateValidation ​
校验,参数校验
具体部分 ​
首页信息 (/wx/home/index) ​
核心点:查询首页需要的信息
响应的JSON字符串可以使用Map来封装
Map<String, Object> entity = new HashMap<>();
try {
entity.put("banner", bannerTask.get());
entity.put("channel", channelTask.get());
entity.put("couponList", couponListTask.get());
entity.put("newGoodsList", newGoodsListTask.get());
entity.put("hotGoodsList", hotGoodsListTask.get());
entity.put("brandList", brandListTask.get());
entity.put("topicList", topicListTask.get());
entity.put("floorGoodsList", floorGoodsListTask.get());
}上面的结果可以通过多线程来分别查询,提高效率 → 每个线程中分别做以上的list的查询
其中SystemConfig.getNewLimit(),SystemConfig.getHotLimit()这些值来源于market_system表中的小程序的相关配置(也可以参考后台管理中的小程序配置)

ExecutorService executorService = Executors.newFixedThreadPool(10);
// 之前后台管理中的广告管理
Callable<List> bannerListCallable = () -> adService.queryIndex();
// 之前后台管理中的类目管理
Callable<List> channelListCallable = () -> categoryService.queryChannel();
// 获取优惠券列表
Callable<List> couponListCallable;
// 判断是否是登录状态
if(userId == null){
//如果是未登录,就查询所有的优惠券中的前三条
couponListCallable = () -> couponService.queryList(0, 3);
} else {
//如果是已经登录,就查询当前用户还未领取的前三张优惠券
couponListCallable = () -> couponService.queryAvailableList(userId,0, 3);
}
// 查询新添加的商品Goods,之后后台管理中的商品管理,商品上架时有选项是否是新品,根据添加时间做排序,limit获取market_system表中的值
Callable<List> newGoodsListCallable = () -> goodsService.queryByNew(0, SystemConfig.getNewLimit());
// 查询热门商品Goods,之后后台管理中的商品管理,商品上架时有选项是否是热卖,根据添加时间做排序,limit获取market_system表中的值
Callable<List> hotGoodsListCallable = () -> goodsService.queryByHot(0, SystemConfig.getHotLimit());
// 之后后台管理中的制造商管理,根据添加时间做排序,limit获取market_system表中的值
Callable<List> brandListCallable = () -> brandService.query(0, SystemConfig.getBrandLimit());
// 之前后台管理中的专题管理,根据添加时间做排序,limit获取market_system表中的值
Callable<List> topicListCallable = () -> topicService.queryList(0, SystemConfig.getTopicLimit());
// 查询类目category以及类目下的商品goods,limit获取market_system表中的值
Callable<List> floorGoodsListCallable = this::getCategoryList;
FutureTask<List> bannerTask = new FutureTask<>(bannerListCallable);
FutureTask<List> channelTask = new FutureTask<>(channelListCallable);
FutureTask<List> couponListTask = new FutureTask<>(couponListCallable);
FutureTask<List> newGoodsListTask = new FutureTask<>(newGoodsListCallable);
FutureTask<List> hotGoodsListTask = new FutureTask<>(hotGoodsListCallable);
FutureTask<List> brandListTask = new FutureTask<>(brandListCallable);
FutureTask<List> topicListTask = new FutureTask<>(topicListCallable);
FutureTask<List> floorGoodsListTask = new FutureTask<>(floorGoodsListCallable);
executorService.submit(bannerTask);
executorService.submit(channelTask);
executorService.submit(couponListTask);
executorService.submit(newGoodsListTask);
executorService.submit(hotGoodsListTask);
executorService.submit(brandListTask);
executorService.submit(topicListTask);
executorService.submit(floorGoodsListTask);最后floorGoodsList要先获取一级类目,然后获得其二级类目,然后获得其二级类目下的商品list,期间根据market_system表中的limit做限制数据量
private List<Map> getCategoryList() {
List<Map> categoryList = new ArrayList<>();
List<CskaoyanMarketCategory> catL1List = categoryService.queryL1WithoutRecommend(0, SystemConfig.getCatlogListLimit());
for (CskaoyanMarketCategory catL1 : catL1List) {
List<CskaoyanMarketCategory> catL2List = categoryService.queryByPid(catL1.getId());
List<Integer> l2List = new ArrayList<>();
for (CskaoyanMarketCategory catL2 : catL2List) {
l2List.add(catL2.getId());
}
List<CskaoyanMarketGoods> categoryGoods;
if (l2List.size() == 0) {
categoryGoods = new ArrayList<>();
} else {
categoryGoods = goodsService.queryByCategory(l2List, 0, SystemConfig.getCatlogMoreLimit());
}
Map<String, Object> catGoods = new HashMap<>();
catGoods.put("id", catL1.getId());
catGoods.put("name", catL1.getName());
catGoods.put("goodsList", categoryGoods);
categoryList.add(catGoods);
}
return categoryList;
}商品详情信息(/wx/goods/detail) ​
核心点:获取商品的详细信息
传入的值是goodsId,然后查询相关的信息
Map<String, Object> data = new HashMap<>();
// goods的信息
data.put("info", info);
// 已经收藏该商品的用户数量
data.put("userHasCollect", userHasCollect);
// 后台管理中的通用问题
data.put("issue", issueCallableTask.get());
// 商品评论
data.put("comment", commentsCallableTsk.get());
// 规格列表 goods_specification表 → 注意按照响应结果的格式来封装
data.put("specificationList", objectCallableTask.get());
// 货品信息 goods_product表
data.put("productList", productListCallableTask.get());
// 参数信息 goods_attribute表
data.put("attribute", goodsAttributeListTask.get());
// 制造商信息
data.put("brand", brandCallableTask.get());
// 是否分享
data.put("share", SystemConfig.isAutoCreateShareImage());
// 商品分享图片地址
data.put("shareImage", info.getShareUrl());可以参考上面/wx/home/index使用多线程来完成,也可以直接写业务代码
相关商品(/wx/goods/related) ​
核心点:根据当前商品查询其相关的商品
请求参数可以直接接收
@GetMapping("related")
public BaseRespVo related(Integer id) {
CskaoyanMarketGoods goods = goodsService.findById(id);
if (goods == null) {
return BaseRespVo.badArgumentValue();
}
// 推荐同类目的其他商品
int cid = goods.getCategoryId();
// 查找六个相关商品
int related = 6;
List<CskaoyanMarketGoods> goodsList = goodsService.queryByCategory(cid, 0, related);
return BaseRespVo.ok(goodsList);
}购物车中货品的数量(/wx/cart/goodscount) ​
核心点:查询购物车中的货品数量
先查询到用户id的信息,然后根据用户用户id查询该用户的购物车中的货品数量
接下来开始继续开发吧
购物车中添加记录(/wx/cart/add) ​
核心点:接收JSON数据对象,添加到数据库中,要绑定用户信息,信息要来源于Shiro,
首先获得用户信息,用户信息参考通用部分/个人信息
判断商品是否存在,
需要判断购物车是否有这个商品的记录,如果已经有的话,需要合并购物车,在已有的购物车记录上添加数量
需要根据规格判断库存
购物车(/wx/cart/index) ​
查看购物车首页,查询当前用户的购物车记录,计算选中的商品(checked=1)的数量和金额
首先获得用户信息,用户信息参考通用部分/个人信息
购物车商品标记(/wx/cart/checked) ​
修改选中商品的选中状态(checked = !checked),然后重新执行/wx/cart/index做的事情,返回结果类型一致
立即购买(/wx/cart/fastadd) ​
做的事情和/wx/cart/add基本上是一样的,add请求如果购物车中已经有这个商品了,可以做合并;fastadd如果购物车中已经有该商品了,可以覆盖掉
这里的返回值是在market_cart表中该记录的id
结账信息(/wx/cart/checkout) ​
生成购物车结账信息
这个接口的计算金额部分会比较繁琐,耐心点儿做
如果cartid=0则是通过购物车下单的,查询当前用户选中的商品(checked=1)的结账信息;如果cartid !=0 则是通过商品详情页立即购买生成的下单信息,然后根据fastadd添加的cart记录计算下单信息
然后
判断地址状态,addressId=0,查询该用户的默认收货地址;不为0查询当前地址
判断优惠券状态,如果为couponId=-1,则查询可用的优惠券数量;如果大于0,则使用指定的优惠券
- 要注意判断优惠券的可用状态:优惠券是否已经删除、该用户是否有该id对应的优惠券、优惠券是否过期、结账总金额是否满足
根据该用户的购物车中选中的记录(checked=1),计算总金额,如果金额超过运费满减金额(后台配置管理),则不添加运费,如果不超过运费满减金额,则需要添加运费,如果可以使用指定优惠券,减去优惠券的金额;也就是
- 实付金额 = 商品合计 - (商品合计 > 运费满减金额?0:运费) - 优惠券金额
提交订单(/wx/order/submit) ​
购物车结账信息再重新计算一次;额外去做的事情添加order和orderGoods记录,由于项目2不做支付,订单状态可以直接定义为待付款,删除购物车中的记录(可以逻辑删除),同时减去库存,如果库存不足可以抛出异常,给出提示信息,如果使用了优惠券,要将优惠券状态标记为已使用
额外说明 ​
订单信息中的handleOption是可以操作的选项,handleOption中的哪些值为true,基本上就是显示哪一些按钮
比如已付款的订单的handleOption

aftersale: 申请售后(这个也可以不管)
cancel:取消订单
comment:去评论(该值为true且该order_goods没有评论的前提下才会显示该按钮)
confirm:确认收货
delete:删除订单
pay:去付款
rebuy:再次购买
refun:申请退款
其中refund为true,意味着申请退款按钮显示

其余 ​
有问题及时问,目前我就是大家的产品经理