苍穹
苍穹

苍穹

业务关系

  • 类别(Category) 是基础分类,分为菜品分类和套餐分类两种类型
  • 菜品(Dish) 属于某个菜品分类,可以有多种口味(DishFlavor)
  • 套餐(Setmeal) 属于某个套餐分类,可以包含多个菜品(SetmealDish关联)

关系图:

类别(Category)
├── 菜品分类
│   └── 菜品(Dish)
│       └── 菜品口味(DishFlavor)
└── 套餐分类
    └── 套餐(Setmeal)
        └── 套餐菜品关系(SetmealDish)

各模块增删查改前提条件

类别(Category)管理

新增
– 无特殊限制,直接创建启用状态的分类

删除
– 分类下不能有关联的菜品或套餐,否则抛出 DeletionNotAllowedException

修改
– 无特殊限制,可修改分类基本信息

启用/停用
– 直接修改分类状态

菜品(Dish)管理

新增
– 无特殊限制,创建菜品及其口味信息

删除
– 菜品必须处于停售状态(DISABLE),否则抛出异常
– 菜品不能被任何套餐关联,否则抛出异常

修改
– 无特殊限制,可修改菜品信息和口味

启用/停用
– 直接修改菜品状态
– 如果停用菜品,且该菜品被套餐关联,则同时停用相关套餐

套餐(Setmeal)管理

新增
– 无特殊限制,创建套餐及关联菜品信息

删除
– 套餐必须处于停售状态,否则抛出 DeletionNotAllowedException

修改
– 无特殊限制,可修改套餐信息和关联菜品

启用/停用
– 启用套餐时,如果套餐内包含停售的菜品,则不能启用,抛出 DeletionNotAllowedException
– 停用套餐无特殊限制

这些设计保证了数据的一致性和业务逻辑的完整性。

数据库表的关系

根据代码分析,数据库表之间的关系如下:

数据库表关系

1. Category(分类表)

  • 作为基础分类表,包含菜品分类和套餐分类
  • type 字段区分分类类型(菜品或套餐)

2. Dish(菜表)

3. DishFlavor(菜品口味表)

  • dish_id 外键关联 Dish 表
  • 存储菜品的各种口味信息

4. Setmeal(套餐表)

5. SetmealDish(套餐菜品关系表)

  • setmeal_id 外键关联 Setmeal 表
  • dish_id 外键关联 Dish 表
  • 实现套餐与菜品的多对多关系

关系图谱

Category <--(category_id)-- Dish <--(dish_id)-- DishFlavor
    ^                         |
    |                         |
Category <--(category_id)-- Setmeal <--(setmeal_id)-- SetmealDish --(dish_id)--> Dish

这种设计实现了分类管理、菜品管理、套餐管理的完整业务关系,支持复杂的关联查询和业务约束检查。

HttpClient

客户端编程工具包

Maven坐标

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

Get请求

HttpClient发送请求步骤:

  • 创建HttpClient对象
  • 创建Http请求对象
  • 调用HttpClient的execute方法发送请求

测试

@Test
    public void test() throws IOException {
        //创建HttpClient对象
        CloseableHttpClient client= HttpClients.createDefault();
        //创建请求对象
        HttpGet httpGet=new HttpGet("http://localhost:8080/user/shop/status");
        //发送请求,接收响应结果
        CloseableHttpResponse response=client.execute(httpGet);

        //解析响应结果
        int statusCode=response.getStatusLine().getStatusCode();
        System.out.println(statusCode);

        HttpEntity entity=response.getEntity();
        String data= EntityUtils.toString(entity);
        System.out.println("返回数据:"+data);

        //释放资源
        response.close();
        client.close();
    }

Post请求

在HttpClientTest中添加POST方式请求方法,相比GET请求来说,POST请求若携带参数需要封装请求体对象,并将该对象设置在请求对象中。

实现步骤:

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接收响应结果
  4. 解析响应结果
  5. 关闭资源

微信开发者小程序

微信开发小程序密钥:ee1bbc131fbe07ca653e1c2d5620f8e3

ID:wx8d43af60809ec22f

image-20251207184310156

步骤分析:

  1. 小程序端,调用wx.login()获取code,就是授权码。
  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
  6. 小程序端,收到自定义登录态,存储storage。
  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。
  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

客户端查找操作

根据分类ID查找菜品和套餐

需求:查找正在启用的套餐和菜品

查询的数据包括两个表的内容时,需要分别定义两个实例返回数据,在一起返回

//根据分类查询菜品(包含口味)
    @Override
    public List<DishVO> typelist(Dish dish) {
        List<Dish> dishList=dishMapper.getTypeId(dish);

        List<DishVO> dishVOS=new ArrayList<>();

        for(Dish d:dishList){
            DishVO dishVO=new DishVO();
            BeanUtils.copyProperties(d,dishVO);

            List<DishFlavor> dishFlavors=dishFlavorMapper.getByDishId(d.getId());
            dishVO.setFlavors(dishFlavors);
            dishVOS.add(dishVO);
        }
        return dishVOS;
    }

Spring Cache

依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

在SpringCache中提供了很多缓存操作的注解,常见的是以下的几个:

注解 说明
@EnableCaching 开启缓存注解功能,通常加在启动类上
@Cacheable 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中
@CachePut 将方法的返回值放到缓存中
@CacheEvict 将一条或多条数据从缓存中删除

在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

微信支付

https://pay.weixin.qq.com/static/product/product_index.shtml

image-20251209181852523

微信支付相关接口:

JSAPI下单:商户系统调用该接口在微信支付服务后台生成预支付交易单(对应时序图的第5步)

image-20251209181925606

微信小程序调起支付:通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付(对应时序图的第10步)

image-20251209182048024

隧道token:Yzk2YWRlNWQtOWFhNS00YmUzLTlkYTItZjYyNDc4YmY1Zjcy

订单管理

1. orders(订单表)

  • 主表,记录订单的基本信息。
  • 字段示例:id, user_id, status, pay_status, amount, address, phone, consignee, order_time, checkout_time, cancel_time 等。
  • 业务关系:
  • 一个订单属于一个用户。
  • 一个订单包含多个订单明细。
  • 一个订单可以有支付、状态变更等操作记录。

2. order_detail(订单明细表)

  • 从表,记录订单中的具体商品信息。
  • 字段示例:id, order_id, dish_id, setmeal_id, name, price, number 等。
  • 业务关系:
  • 一个订单对应多个订单明细。
  • 一个订单明细关联一个菜品或套餐。

3. address_book(地址簿表)

  • 用户地址表,记录用户的收货地址。
  • 字段示例:id, user_id, consignee, phone, detail, city_name, district_name 等。
  • 业务关系:
  • 一个用户可以有多个地址。
  • 一个订单关联一个地址。

4. shopping_cart(购物车表)

  • 用户购物车表,记录用户加入购物车的商品。
  • 字段示例:id, user_id, dish_id, setmeal_id, name, price, number, create_time 等。
  • 业务关系:
  • 一个用户对应一个购物车(逻辑上)。
  • 购物车中的商品在提交订单后会清空。

5. user(用户表)

  • 用户表,记录用户信息。
  • 字段示例:id, openid, name, phone, status, create_time 等。
  • 业务关系:
  • 一个用户可以下多个订单。
  • 一个用户可以有多个地址。

业务操作流程

用户端:

  1. 查看购物车 → shopping_cart
  2. 选择地址 → address_book
  3. 提交订单 → 创建 orders 记录,写入 order_detail,清空 shopping_cart
  4. 支付订单 → 更新 orders 中的支付状态
  5. 查看历史订单 → 关联查询 orders + order_detail
  6. 取消/再来一单 → 更新 orders 状态或复制到购物车

管理端:

  1. 订单搜索/统计 → 查询 orders 并统计状态
  2. 接单/拒单/派送/完成 → 更新 orders 状态
  3. 查看订单详情 → 关联查询 orders + order_detail

SpringTask

定时触发一些任务

cron表达式

cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间

构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义

每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)

cron表达式在线生成器:https://cron.qqe2.com/

通配符:

* 表示所有值;

? 表示未说明的值,即不关心它为何值;

– 表示一个指定的范围;

, 表示附加一个可能值;

/ 符号前表示开始时间,符号后表示每次递增的值;

cron表达式案例:

*/5 * * * * ? 每隔5秒执行一次

0 */1 * * * ? 每隔1分钟执行一次

0 0 5-15 * * ? 每天5-15点整点触发

0 0/3 * * * ? 每三分钟触发一次

0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发

0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发

0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发

0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时

0 0 10,14,16 * * ? 每天上午10点,下午2点,4点

订单状态定时处理

@Scheduled(cron = "0 * * * * ?")
    public void processTimeoutOrder() {
        log.info("处理支付超时订单:{}", new Date());
        LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(-15);
        List<Orders> ordersList = orderMapper.processSelect(Orders.PENDING_PAYMENT, localDateTime);
        if (ordersList != null && !ordersList.isEmpty()) {
            ordersList.forEach(orders -> {
                orders.setStatus(Orders.CANCELLED);
                orders.setCancelTime(LocalDateTime.now());
                orders.setCancelReason("支付超时,取消订单");
                orderMapper.update(orders);
            });
        }
    }

WebSocket

WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。

HTTP协议和WebSocket协议对比:

  • HTTP是短连接
  • WebSocket是长连接
  • HTTP通信是单向的,基于请求响应模式
  • WebSocket支持双向通信
  • HTTP和WebSocket底层都是TCP连接

WebSocket缺点:

服务器长期维护长连接需要一定的成本
各个浏览器支持程度不一
WebSocket 是长连接,受网络限制比较大,需要处理好重连

结论:WebSocket并不能完全取代HTTP,它只适合在特定的场景下使用

maven坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

服务端组件

@Component
@ServerEndpoint(“/ws/{sid}”)
public class WebSocketServer {

//存放会话对象
private static Map<String, Session> sessionMap = new HashMap();

/**
 * 连接建立成功调用的方法
 */
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
    System.out.println("客户端:" + sid + "建立连接");
    sessionMap.put(sid, session);
}

/**
 * 收到客户端消息后调用的方法
 *
 * @param message 客户端发送过来的消息
 */
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
    System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}

/**
 * 连接关闭调用的方法
 *
 * @param sid
 */
@OnClose
public void onClose(@PathParam("sid") String sid) {
    System.out.println("连接断开:" + sid);
    sessionMap.remove(sid);
}

/**
 * 群发
 *
 * @param message
 */
public void sendToAllClient(String message) {
    Collection<Session> sessions = sessionMap.values();
    for (Session session : sessions) {
        try {
            //服务器向客户端发送消息
            session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

配置类

/**
 * WebSocket配置类,用于注册WebSocket的Bean
 */
@Configuration
public class WebSocketConfiguration {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

Apache POI

操作Excel文件

maven坐标

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.16</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.16</version>
</dependency>

相关条目

  • javaweb
  • Mybatis-plus
  • Mysql
  • redis

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注