1.nginx
2. 公共字段自动填充
/**
* 自定义切面,实现公共字段自动填充处理逻辑
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {}
/**
* 前置通知,在通知中进行公共字段的赋值
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始自动填充公共字段..");
//获取到当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = autoFill.value();//获得注解中的数据库操作类型
//获取到当前被拦截的方法的参数--实体对象
Object[] args = joinPoint.getArgs();
if(args == null || args.length == 0){
return;
}
Object entity = args[0];
//准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//根据不同的操作类型,为对应的属性反射来赋值
if(operationType == OperationType.INSERT){
//为4个公共字段来赋值
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象属性赋值
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
e.printStackTrace();
}
}else if(operationType == OperationType.UPDATE){
//为2个公共字段来赋值
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象属性赋值
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3.新增菜品和对应口味数据(业务逻辑层代码)
/**
* 新增菜品和对应的口味数据
* @param dishDTO
*/
@Transactional
public void saveWithFlavor(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO, dish);
//向菜品表插入1条数据
dishMapper.insert(dish);
//获取insert语句生成的主键值
Long dishId = dish.getId();
List<DishFlavor> flavors = dishDTO.getFlavors();
if(flavors!= null && flavors.size() > 0){
flavors.forEach(dishFlavor -> {
dishFlavor.setDishId(dishId);
});
//向口味表插入n条数据
dishFlavorMapper.insertBatch(flavors);
}
}
4.批量删除菜品
@Transactional
public void deleteBatch(List<Long> ids) {
//判断当前菜品是否能删除---
//判断是否存在起售中的菜品
for (Long id : ids) {
Dish dish = dishMapper.getById(id);
if(dish.getStatus() == StatusConstant.ENABLE){
//当前菜品处于起售中,不能删除
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//判断当前菜品是否被套餐关联
List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishId(ids);
if(setmealIds!= null && setmealIds.size() > 0){
//当前菜品被套餐关联,不能删除
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
// //删除菜品表中的菜品数据
// for (Long id : ids) {
// dishMapper.deleteById(id);
// //删除菜品关联的口味数据
// dishFlavorMapper.deleteByDishId(id);
// }
//根据菜品id集合批量删除菜品数据
//sql:delete from dish where id in (?,?,?)
dishMapper.deleteByIds(ids);
//根据菜品id集合批量删除关联的口味数据
//sql:delete from dish_flavor where dish_id in (?,?,?)
dishFlavorMapper.deleteByDishIds(ids);
}
5.Redis
数据类型:String、hash(HashMap)、list(LinkedList)、set(HashSet)、sorted set
6.Java操作Redis数据库
@Test
public void testRedisTemplate() {
System.out.println(redisTemplate);
ValueOperations valueOperations = redisTemplate.opsForValue();
HashOperations hashOperations = redisTemplate.opsForHash();
ListOperations listOperations = redisTemplate.opsForList();
SetOperations setOperations = redisTemplate.opsForSet();
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}
/**
* 操作字符串类型的数据
*/
@Test
public void testString() {
// set get setex setnx
redisTemplate.opsForValue().set("name", "门哥");
String city = (String) redisTemplate.opsForValue().get("name");
System.out.println(city);
redisTemplate.opsForValue().set("code", "1234", 60, TimeUnit.SECONDS);
redisTemplate.opsForValue().setIfAbsent("lock", 1);
redisTemplate.opsForValue().setIfAbsent("lock", 2);
}
/**
* 操作hash类型的数据
*/
@Test
public void testHash() {
// hset hget hdel hkeys hvals
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("100", "name", "tom");
hashOperations.put("100", "age", 20);
String name = (String) hashOperations.get("100", "name");
System.out.println(name);
Set keys = hashOperations.keys("100");
System.out.println(keys);
List values = hashOperations.values("100");
System.out.println(values);
hashOperations.delete("100", "age");
}
/**
* 操作列表类型的数据
*/
@Test
public void testList() {
// lpush lrange lpop rpush
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPushAll("mylist", "a", "b", "c");
listOperations.leftPush("mylist", "d");
List mylist = listOperations.range("mylist", 0, -1);
System.out.println(mylist);
listOperations.rightPop("mylist");
Long size = listOperations.size("mylist");
System.out.println(size);
}
/**
* 操作集合类型的数据
*/
@Test
public void testSet() {
// sadd smembers scard sinter sunion srem
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("set1", "a", "b", "c", "d");
setOperations.add("set2", "a", "b", "x", "y");
Set members = setOperations.members("set1");
System.out.println(members);
Long size = setOperations.size("set1");
System.out.println(size);
Set intersect = setOperations.intersect("set1", "set2");
System.out.println(intersect);
Set union = setOperations.union("set1", "set2");
System.out.println(union);
setOperations.remove("set1", "a", "b");
}
/**
* 操作有序集合类型的数据
*/
@Test
public void testZSet() {
// zadd zrange zrem zcard
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
zSetOperations.add("zset1", "a", 10);
zSetOperations.add("zset1", "b", 12);
zSetOperations.add("zset1", "c", 9);
Set zset1 = zSetOperations.range("zset1", 0, -1);
System.out.println(zset1);
zSetOperations.incrementScore("zset1", "c", 10);
zSetOperations.remove("zset1", "a", "b");
}
/**
* 通用命令操作
*/
@Test
public void testCommon() {
Set keys = redisTemplate.keys("*");
System.out.println(keys);
Boolean name = redisTemplate.hasKey("name");
Boolean set1 = redisTemplate.hasKey("set1");
for (Object key : keys) {
DataType type = redisTemplate.type(key);
System.out.println(type.name());
}
redisTemplate.delete("mylist");
}
}
7.店铺营业状态接口
/**
* 设置店铺营业状态
* @param status
* @return
*/
@PutMapping("/{status}")
@ApiOperation("设置店铺营业状态")
public Result setStatus(@PathVariable Integer status) {
log.info("设置营业状态为:{}", status == 1 ? "营业中" : "打烊中");
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("SHOP_STATUS",status);
return Result.success();
}
/**
* 获取店铺营业状态
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺营业状态")
public Result<Integer> getStatus() {
Integer status = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");
log.info("获取店铺营业状态:{}",status == 1 ? "营业中" : "打烊中");
return Result.success(status);
}
}
8.HttpClient
/**
* 测试通过httpclient发送GET方式的请求
*/
@Test
public void testGET() throws IOException {
//创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 创建http请求对象
HttpGet httpGet = new HttpGet("http://localhost:8088/user/shop/status");
//发送请求,接受响应结果
CloseableHttpResponse response = httpClient.execute(httpGet);
//获取服务端返回的状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("服务端返回的状态码:" + statusCode);
HttpEntity entity = response.getEntity();
String body = EntityUtils.toString(entity);
System.out.println("服务端返回的结果:" + body);
//关闭资源
response.close();
httpClient.close();
}
/**
* 测试通过httpclient发送POST方式的请求
*/
@Test
public void testPOST() throws IOException, JSONException {
// 创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 创建http请求对象
HttpPost httpPost = new HttpPost("http://localhost:8088/admin/employee/login");
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", "admin");
jsonObject.put("password", "123456");
StringEntity entity = new StringEntity(jsonObject.toString());
//指定请求编码方式
entity.setContentEncoding("UTF-8");
//数据格式
entity.setContentType("application/json");
httpPost.setEntity(entity);
//发送请求
CloseableHttpResponse response = httpClient.execute(httpPost);
//解析返回结果
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("服务端返回的状态码:" + statusCode);
HttpEntity entity1 = response.getEntity();
String body = EntityUtils.toString(entity1);
System.out.println("服务端返回的结果:" + body);
//关闭资源
response.close();
httpClient.close();
}
}
9.微信登录功能接口
//路由层
@PostMapping("/login")
@ApiOperation("微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
log.info("用户登录接口,参数:{}", userLoginDTO.getCode());
// 微信登录
User user = userService.wxLogin(userLoginDTO);
//为微信用户生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID,user.getId());
String token =JwtUtil.createJWT
(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);
UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
//业务层
//微信登录接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private UserMapper userMapper;
/**
* 微信登录
* @param userLoginDTO
* @return
*/
public User wxLogin(UserLoginDTO userLoginDTO) {
String openid = getOpenid(userLoginDTO.getCode());
//判断openid是否为空
if (openid == null) {
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
//判断当前用户是否为新用户
User user = userMapper.getByOpenid(openid);
//如果是新用户,自动完成注册
if (user == null) {
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.insert(user);
}
//返回这个用户对象
return user;
}
/**
* 调用微信接口服务,获取微信用户的openid
* @param code
* @return
*/
private String getOpenid(String code) {
//调用微信接口服务,获得当前微信用户的openid
Map<String, String> map = new HashMap<>();
map.put("appid",weChatProperties.getAppid());
map.put("secret",weChatProperties.getSecret());
map.put("js_code",code);
map.put("grant_type","authorization_code");
String json = HttpClientUtil.doGet(WX_LOGIN, map);
JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString("openid");
return openid;
}
10.清理菜品缓存方法
/**
* 清理缓存数据
* @param pattern
*/
private void clearCache(String pattern){
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
11.Spring Cache
是一个框架,实现了基于注解的缓存功能,只需要加一个注解
@CachePut
@PostMapping
@CachePut(cacheNames = "userCache",key = "#user.id") //如果使用Spring Cache缓存数据,key的生成;userCache::abc
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
12.购物车功能开发
添加购物车
/**
* 添加购物车
* @param shoppingCartDTO
*/
public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
//判断当前加入到购物车的商品是否已经存在了
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);
Long userId = BaseContext.getCurrentId();
shoppingCart.setUserId(userId);
List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);
//如果已经存在了,只需要将数量加1
if(list!= null && list.size() > 0){
ShoppingCart cart = list.get(0);
cart.setNumber(cart.getNumber() + 1);
shoppingCartMapper.updateNumberById(cart);
}else{
//如果不存在,需要插入一条购物车数据
//来判断本次添加到购物车的是菜品还是套餐
Long dishId = shoppingCartDTO.getDishId();
if (dishId!= null){
//本次添加到购物车的是菜品
Dish dish = dishMapper.getById(dishId);
shoppingCart.setName(dish.getName());
shoppingCart.setAmount(dish.getPrice());
shoppingCart.setImage(dish.getImage());
}else{
//本次添加到购物车的是套餐
Long setmealId = shoppingCartDTO.getSetmealId();
Setmeal setmeal = setmealMapper.getById(setmealId);
shoppingCart.setName(setmeal.getName());
shoppingCart.setAmount(setmeal.getPrice());
shoppingCart.setImage(setmeal.getImage());
}
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCartMapper.insert(shoppingCart);
}
}
13.用户下单功能
/**
* 用户下单
* @param ordersSubmitDTO
* @return
*/
public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
//1.处理各种业务异常(地址簿为空、购物车数据为空)
AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
if (addressBook == null) {
//抛出业务异常
throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
}
//查询当前用户的购物车数据
Long userId = BaseContext.getCurrentId();
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.setUserId(userId);
List<ShoppingCart> shoppingCartList = shoppingCartMapper.list(shoppingCart);
if (shoppingCartList == null || shoppingCartList.size() == 0){
//抛出业务异常
throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
}
//2.向订单表插入1条数据
Orders orders = new Orders();
BeanUtils.copyProperties(ordersSubmitDTO, orders);
orders.setOrderTime(LocalDateTime.now());
orders.setPayStatus(Orders.UN_PAID);
orders.setStatus(Orders.PENDING_PAYMENT);
orders.setNumber(String.valueOf(System.currentTimeMillis()));
orders.setPhone(addressBook.getPhone());
orders.setConsignee(addressBook.getConsignee());
orders.setUserId(userId);
orderMapper.insert(orders);
ArrayList<OrderDetail> orderDetailsList = new ArrayList<>();
//3.向订单详情表插入n条数据
for (ShoppingCart cart : shoppingCartList) {
OrderDetail orderDetail = new OrderDetail();//订单明细
BeanUtils.copyProperties(cart, orderDetail);
orderDetail.setOrderId(orders.getId());//设置当前订单明细关联的订单id
orderDetailsList.add(orderDetail);
}
orderDetailMapper.insertBatch(orderDetailsList);
//4.清空当前用户的购物车数据
shoppingCartMapper.deleteByUserId(userId);
//5.封装VO对象并返回
OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
.id(orders.getId())
.orderTime(orders.getOrderTime())
.orderNumber(orders.getNumber())
.orderAmount(orders.getAmount())
.build();
return orderSubmitVO;
}
14.WebSocket
应用场景:视频弹幕、网页聊天、体育实况更新、股票基金报价实时更新
15.ECharts
基于JavaScript的数据可视化图表库
16.订单统计功能
/**
* 统计指定时间区间内的订单数据
* @param begin
* @param end
* @return
*/
@Override
public OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {
//存放从begin到end的日期列表
List<LocalDate> dateList = new ArrayList<>();
dateList.add(begin);
while (!begin.equals(end)){
begin = begin.plusDays(1);
dateList.add(begin);
}
//存放每天订单总数
List<Integer> orderCountList = new ArrayList<>();
//存放每天有效订单数
List<Integer> validOrderCountList = new ArrayList<>();
//遍历集合
for (LocalDate date : dateList) {
//查询每天订单总数 select count(id) from order where order_time > ? and order_time < ?
LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
Integer orderCount = getOrderCount(beginTime, endTime, null);
//查询每天有效订单数 select count(id) from order where order_time > ? and order_time < ? and status = 5
Integer validOrderCount = getOrderCount(beginTime, endTime, Orders.COMPLETED);
orderCountList.add(orderCount);
validOrderCountList.add(validOrderCount);
}
//计算时间区间内的订单总数量
Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();
//计算时间区间内的有效订单数量
Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();
Double orderCompletionRate = 0.0;
if (totalOrderCount != 0){
//计算订单完成率
orderCompletionRate = (double) validOrderCount / totalOrderCount;
}
return OrderReportVO.builder()
.dateList(StringUtils.join(dateList, ","))
.orderCountList(StringUtils.join(orderCountList, ","))
.validOrderCountList(StringUtils.join(validOrderCountList, ","))
.totalOrderCount(totalOrderCount)
.validOrderCount(validOrderCount)
.orderCompletionRate(orderCompletionRate)
.build();
}
/**
* 根据条件统计订单数量
* @param begin
* @param end
* @param status
* @return
*/
private Integer getOrderCount(LocalDateTime begin, LocalDateTime end, Integer status){
Map map = new HashMap();
map.put("begin", begin);
map.put("end", end);
map.put("status", status);
return orderMapper.countByMap(map);
}
}