买家端(手机端) 卖家端(PC端)
详细设计个各个功能模块
买家 卖家
商品: 商品列表数据
订单: 订单创建 订单查询 订单取消 ...
类目: 订单管理 商品管理 类目管理 ...
SSH 框架的了解和使用
Mybatis 的两种配置方法
Redis 的缓存作用 分布式锁
Aop 身份验证
等等
设计表的成员 表与表之间的关系 确定每个表的主键 设计属性之间的关联...
这里用Slf4j
1.导入依赖
2.加上注解
@Slf4j
3..使用
log.error("【创建订单】购物车不能为空");
log.info("【微信支付】发起支付request={}",JsonUtil.toJson(payRequest));
logging:
pattern:
console: "%d - %msg%n"
path: /var/log/tomcat/
file: /var/log/tomcat/sell.log
level:
com.imooc.LoggerTest: debug
path 和 file 二选一即可
Path:产生文件的位置
File:产生文件的位置和名字
Level: 指定某个类的日志级别
Resouces目录下建立 Logback-spring.xml配置文件
xml version="1.0" encoding="UTF-8" ?>
%d - %msg%n
%msg%n
%msg%n
简单的日志需求选择第一种即可,复杂的需要选择第二种配置
注意: 这里写配置文件时提示不是很友好,不要打错!
添加依赖:
Mysql的驱动
Jap的驱动
配置数据库的信息:
在application.yml配置文件中`配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 741623
url: jdbc:mysql://127.0.0.1:3306/food?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull
jpa:
show-sql: true
对象类需要加入
@Entity 注解
导入包 import javax.persistence.Entity
默认下类名和表明一致
类名中除了第一个字母,其他字母的大写在表中会自动用_加对应的小写字母替代
表名: product_category
类名:ProductCategory
也可以加注解来添加映射关系
@Table(name = “ product_category”)
对象里面的属性名也必须和数据表里面的属性名对应
否则需要在属性之前添加注解@Column(name = “属性名”)
对象的主键用 @Id 标识 导入下面的包
import javax.persistence.Id;
对象自增用 @GeneratedValue 表示
import javax.persistence.GeneratedValue;
若类名和属性名都与数据表对应, 则这个类就和 product_category数据表建立映射关系.
注意: 建议用默认值
类名前面添加 @Data
导入包import lombok.Data;
依赖文件和之前日志的依赖一样
这个注解可以省去 get 和 set 方法
DAO 层的编写
与对象做关联
public interface ProductCategoryRepository extends JpaRepository
继承接口 JpaRepository<> 第一个参数表明对象类的名称 , 第二个参数表明对象的主键
在接口中列出可能需要用的方法
再建立CategoryServiceImpl类实现接口 CategoryService
在 CategoryServiceImpl 需要加入注解 @Service
导入包import org.springframework.stereotype.Service;
由于类目不存在单独给买家端的接口,所以不存在 controller
基本步骤和上面买家类目实现一样
注重Service的编写
Dao ---> Service ---> Controller
------------------技术技巧------------------------------------------------
查询的时候传入 Pageable pageable对象可以分页查询
Page
导入包 import org.springframework.data.domain.Pageable;
传入Pageable pageable对象 返回的时一个 Page<>对象
当用数字表示一些商品的情况或者订单的情况时,一旦数目过多就容易混淆,
这时候可以用到枚举类 Enum, 可以建立一个单独的包来管理各个枚举类
枚举类可以将对应的数字信息和和文字信息结合起来,可以更加方便的了解数字对应的信息
package com.imooc.enums;
import lombok.Getter;
/**
* 商品状态
* Created by 廖师兄
* 2017-05-09 17:33
*/
@Getter
public enum ProductStatusEnum implements CodeEnum {
UP(0, "在架"),
DOWN(1, "下架")
;
private Integer code;
private String message;
ProductStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
返回给前端的类目信息分三层结构
package com.imooc.VO;
import lombok.Data;
/**
* http请求返回的最外层对象
* Created by 廖师兄
* 2017-05-12 14:13
*/
@Data
public class ResultVO<T> {
/** 错误码. */
private Integer code;
/** 提示信息. */
private String msg;
/** 具体内容. */
private T data;
}
package com.imooc.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 商品(包含类目)
* Created by 廖师兄
* 2017-05-12 14:20
*/
@Data
public class ProductVO {
@JsonProperty("name")
private String categoryName;
@JsonProperty("type")
private Integer categoryType;
@JsonProperty("foods")
private List
}
package com.imooc.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* 商品详情
* Created by 廖师兄
* 2017-05-12 14:25
*/
@Data
public class ProductInfoVO {
@JsonProperty("id")
private String productId;
@JsonProperty("name")
private String productName;
@JsonProperty("price")
private BigDecimal productPrice;
@JsonProperty("description")
private String productDescription;
@JsonProperty("icon")
private String productIcon;
}
这里用到一个 lambda 表达式(从商品对象中获得类目属性)
List
.map(e -> e.getCategoryType())
.collect(Collectors.toList());
作用是从 对象集合productInfoList中抽取一个属性集合 categoryTypeList
作用等价于:
List
传统方法
for (ProductInfo productInfo : productInfoList) {
categoryTypeList.add(productInfo.getCategoryType());
}
根据获得的类目type属性集合来得到类目对象集合
List
新建一个ProductVO(商品类目信息: 包含 类目名字 , 类目编号 , 商品信息 List )的 List
List
对类目对象集合循环遍历
for (ProductCategory productCategory: productCategoryList) {
新建一个ProductVO对象
设置CategoryType和CategoryName 属性值
ProductVO productVO = new ProductVO();
productVO.setCategoryType(productCategory.getCategoryType());
productVO.setCategoryName(productCategory.getCategoryName());
新建一个 ProductInfoVO 的List --->roductInfoVOList
List
在类目循环体内部对商品信息金额和循环遍历
for (ProductInfo productInfo: productInfoList) {
If 判断 : 若满足商品对象的类目属性与当前类目对象的类目属性相同
if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {
满足上述 if 判断则新建一个 productInfoVO 对象
运用工具类BeanUtils中的copyProperties()方法将productInfo中的属性值拷贝到productInfoVO对象中(拷贝的值包括两个对象中名字和类型一样的属性)
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
设置 productVO 的 ProductInfoVOList 的值为 productInfoVOList
productVO.setProductInfoVOList(productInfoVOList);
将 productVO 添加到 productVOList
productVOList.add(productVO);
Dao --- service --- controller
都是相关逻辑的处理 不一一列举了
涉及两张数据表(订单用户信息表 和订单商品详情表)
DAO层
订单表接口的方法
Page
订单详情表接口的方法
List
买家订单的service接口的方法
/** 创建订单. */
OrderDTO create(OrderDTO orderDTO);
/** 查询单个订单. */
OrderDTO findOne(String orderId);
/** 查询订单列表. */
Page
/** 取消订单. */
OrderDTO cancel(OrderDTO orderDTO);
/** 完结订单. */
OrderDTO finish(OrderDTO orderDTO);
/** 支付订单. */
OrderDTO paid(OrderDTO orderDTO);
/** 查询订单列表. */
Page
JSON格式转化为 List
导入依赖
Json格式的 orderForm.getItems() 转化为 List
Gson gson = new Gson();
orderDetailList = gson.fromJson(orderForm.getItems(),
new TypeToken>() {
}.getType());
注: 必须时服务号才能进行微信相关接口开发,订阅号不行
2.获取 code(code作为换取access_token的票据,每次用户授权带上的code不一样,code只能使用一次,5分钟未使用自动过期).
3.通过code 获得网页受权access_token
这里是可能是目前最好最全的微信Java开发工具包(SDK)
包括微信支付、开放平台、公众号、企业微信、企业号、小程序等
https://github.com/Wechat-Group/weixin-java-tools
添加Maven引用
注意:以下为最新正式版,最新测试版本号为
· 各模块的artifactId:
o 微信小程序:weixin-java-miniapp
o 微信支付:weixin-java-pay
o 微信开放平台:weixin-java-open
o 公众号:weixin-java-mp
o 企业号/企业微信:weixin-java-cp
注:由于没有服务号,这一块没有研究
--->service ----->controller
由于之前买家端写了很多代码了,其中与数据交互的dao层在卖家端不用写了
/** 创建订单. */
OrderDTO create(OrderDTO orderDTO);
/** 查询单个订单. */
OrderDTO findOne(String orderId);
/** 查询订单列表. */
Page
/** 取消订单. */
OrderDTO cancel(OrderDTO orderDTO);
/** 完结订单. */
OrderDTO finish(OrderDTO orderDTO);
/** 支付订单. */
OrderDTO paid(OrderDTO orderDTO);
/** 查询订单列表. */
Page
添加一个查询所有用户的订单方法(分页查询)
(不予具体说明)
旨在支持应用程序和服务的开发,可以利用物理架构由多个自治的处理元素,不共享主内存,但是通过网络发送消息合作.
三个特点: 1.多节点
2.消息通信(http通信)
3.不共享内存
三个概念: 1.分布式系统
2.集群
3.分布式计算
@Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" +
"&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
public void verify() {}
@Before("verify()")
public void doVerify() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//查询cookie
Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
if (cookie == null) {
log.warn("【登录校验】Cookie中查不到token");
throw new SellerAuthorizeException();
}
//去redis里查询
String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));
if (StringUtils.isEmpty(tokenValue)) {
log.warn("【登录校验】Redis中查不到token");
throw new SellerAuthorizeException();
}
}
//拦截登录异常
//http://sell.natapp4.cc/sell/wechat/qrAuthorize?returnUrl=http://sell.natapp4.cc/sell/seller/login
@ExceptionHandler(value = SellerAuthorizeException.class)
public ModelAndView handlerAuthorizeException() {
return new ModelAndView("redirect:"
.concat(projectUrlConfig.getWechatOpenAuthorize())
.concat("/sell/wechat/qrAuthorize")
.concat("?returnUrl=")
.concat(projectUrlConfig.getSell())
.concat("/sell/seller/login"));
}
导入依赖
创建一个mapper 包
包下建立对应实体类的sql语句操作类
1.通过map的方式插入 sql 语句(一般不用)
//通过 map 的方式
@Insert("insert into product_category(category_name, category_type) values (#{category_name, jdbcType=VARCHAR}, #{category_type, jdbcType=INTEGER})")
int insertByMap(Map
2.通过对象的方式插入 sql 语句
//通过对象的方式
//插入
@Insert("insert into product_category(category_name, category_type) values (#{categoryName, jdbcType=VARCHAR}, #{categoryType, jdbcType=INTEGER})")
int insertByObject(ProductCategory productCatrgory);
3.通过类型查询
//通过类型查询
@Select("select *from product_category where category_type = #{categoryType}")
@Results({
@Result(column = "category_type" , property = "categoryType"),
@Result(column = "category_id" , property = "categoryId"),
@Result(column = "category_name" , property = "categoryName")
})
ProductCategory findByCategoryType(Integer categoryType);
4.通过名字查询
//通过名字查询
@Select("select *from product_category where category_name = #{categoryName}")
@Results({
@Result(column = "category_type" , property = "categoryType"),
@Result(column = "category_id" , property = "categoryId"),
@Result(column = "category_name" , property = "categoryName")
})
List
5.更新姓名
(传入姓名和type)
//更新姓名
@Update("update product_category set category_name = #{categoryName} where category_type = #{categoryType}")
int updateByCategoryType(@Param("categoryName") String categoryName, @Param("categoryType") Integer categoryType);
(传入对象)
@Update("update product_category set category_name = #{categoryName} where category_type = #{categoryType}")
int updateByObject(ProductCategory productCategory);
6.通过categoryType 删除
@Delete("delete from product_category where category_type = #{categoryType}")
int deleteByCategoryType(Integer categoryType);
Namespace : 接口的位置
Type: 操作的实体类的位置
Id : 方法名
resuletMap: 上面定义的 resuletMap标签
parameterType : 写入参的类型 如果入参时个对象,就写入参的路径
完整xml文件
xml version="1.0" encoding="UTF-8"?>
mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
接下来在配置文件里面配置
mybatis:
mapper-locations: classpath:mapper/*.xml
在启动类中配置配置文件的位置
@MapperScan(basePackages = "com.cyg.dataobject.mapper")
推荐一个网站
www.redis.cn
package com.cyg.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @author cyg
* @date 18-3-17 下午5:14
**/
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
public boolean lock(String key , String value){
if (redisTemplate.opsForValue().setIfAbsent(key,value)){
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期
if(!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()){
//获取上一个锁的时间
String oldValue = redisTemplate.opsForValue().getAndSet(key , value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(System.currentTimeMillis())){
return true;
}
}
return false;
}
public void unlock(String key , String value){
try {
String currentValue = redisTemplate.opsForValue(.get(key);
if (StringUtils.isEmpty(currentValue) && currentValue.equals(value)){
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e){
log.error("[redis分布式锁] 解锁异常");
}
}
}