第三章 智能库存调拨优化系统——前置准备与基础框架

一、系统概述

1.1 业务场景

在多仓库的仓储管理体系中,不同仓库之间的库存往往存在不平衡的问题:某些仓库某种商品积压严重,而另一些仓库却面临缺货风险。传统的人工调拨方式依赖管理员经验判断,存在响应慢、决策主观、成本高等问题。

本系统通过引入大语言模型(LLM)技术,构建一套ERP智能库存调拨系统,实现以下核心价值:

  • AI智能预测:LLM分析历史销售数据与库存分布,自动预测缺货风险
  • 人类反馈闭环:AI生成调拨建议后,由人工审核确认,确保决策可靠性
  • 自动化调拨流程:审核通过后自动生成库存调拨单,实现从分析到执行的全链路自动化
  • 效率提升:大幅降低人工分析成本,提升仓储调拨效率和准确性

1.2 系统功能模块

模块 功能描述
库存分析 实时汇总各仓库库存分布情况
销售预测 基于历史销售数据预测未来缺货风险
调拨建议 AI综合分析后生成智能调拨建议
人工审核 提供调拨建议的审核界面,支持通过/驳回
调拨执行 审核通过后自动生成调拨单并扣减库存
库存日志 记录所有库存变动操作,确保可追溯

1.3 系统亮点

  • AI预测 + 人类反馈(Human-in-the-Loop):结合AI的智能分析能力与人类的业务经验,既保证效率又确保准确性
  • 自动化流程:从数据分析到调拨单生成的全流程自动化,减少人工干预
  • 完整的库存追踪:通过库存操作日志表记录每一次库存变动,确保库存数据可追溯

二、表结构设计

本节将创建6张核心表,涵盖仓库、商品、销售记录、库存、调拨单和库存日志。这些表构成了智能库存调拨系统的数据基础。

2.1 仓库信息表(bb_warehouse)

用于存放仓库的基本信息,包括仓库编码、名称、位置和负责人。

CREATE TABLE bb_warehouse (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  warehouse_code VARCHAR(50) NOT NULL UNIQUE COMMENT '仓库编码',
  warehouse_name VARCHAR(100) NOT NULL COMMENT '仓库名称',
  location VARCHAR(255) DEFAULT NULL COMMENT '仓库位置描述',
  manager VARCHAR(50) DEFAULT NULL COMMENT '负责人',
  status TINYINT DEFAULT 1 COMMENT '状态(1:启用,0:停用)',
  created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仓库信息表';

2.2 商品信息表(bb_product)

记录商品的基本信息,包括商品编码、名称、规格型号、计量单位和分类。

CREATE TABLE bb_product (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  product_code VARCHAR(50) NOT NULL UNIQUE COMMENT '商品编码',
  product_name VARCHAR(100) NOT NULL COMMENT '商品名称',
  spec VARCHAR(100) DEFAULT NULL COMMENT '规格型号',
  unit VARCHAR(20) DEFAULT '件' COMMENT '计量单位',
  category VARCHAR(50) DEFAULT NULL COMMENT '分类',
  status TINYINT DEFAULT 1 COMMENT '状态(1:启用,0:停用)',
  created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

2.3 商品销售记录表(bb_sales_record)

记录每个仓库中每种商品的销售数据,是AI进行销售趋势分析和缺货预测的核心数据源。

CREATE TABLE bb_sales_record (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
  product_id BIGINT NOT NULL COMMENT '商品ID',
  sale_date DATE NOT NULL COMMENT '销售日期',
  quantity DECIMAL(18,2) NOT NULL COMMENT '销售数量',
  revenue DECIMAL(18,2) DEFAULT 0 COMMENT '销售金额',
  created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品销售记录表';

2.4 库存表(bb_inventory)

记录每个仓库中每种商品的当前库存数量和锁定库存数量。锁定库存用于标识调拨中未完成的部分,避免超卖。

CREATE TABLE bb_inventory (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
  product_id BIGINT NOT NULL COMMENT '商品ID',
  quantity DECIMAL(18,2) DEFAULT 0 COMMENT '当前库存数量',
  locked_quantity DECIMAL(18,2) DEFAULT 0 COMMENT '锁定库存数量(例如调拨中未完成的部分)',
  updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  UNIQUE KEY uk_inventory_wh_prod (warehouse_id, product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表';

设计说明uk_inventory_wh_prod 唯一索引确保同一仓库中同一商品只有一条库存记录,locked_quantity 字段用于处理并发调拨场景。

2.5 调拨单主表(bb_transfer_order)

调拨单主表记录调拨的基本信息,包括调出仓库、调入仓库、调拨状态和类型。

CREATE TABLE bb_transfer_order (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  order_no VARCHAR(50) NOT NULL UNIQUE COMMENT '调拨单号',
  source_warehouse_id BIGINT NOT NULL COMMENT '调出仓库ID',
  target_warehouse_id BIGINT NOT NULL COMMENT '调入仓库ID',
  status TINYINT DEFAULT 0 COMMENT '状态(0:待审核,1:已审核,2:调拨中,3:已完成,4:已取消)',
  transfer_type INT DEFAULT 0 COMMENT '调拨类型:1智能,0人工',
  transfer_date DATE DEFAULT NULL COMMENT '调拨日期',
  comment TEXT COMMENT '说明',
  created_by VARCHAR(50) DEFAULT NULL COMMENT '创建人',
  approved_by VARCHAR(50) DEFAULT NULL COMMENT '审核人',
  created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='调拨单主表';

字段说明transfer_type 字段区分AI智能调拨(值为1)和传统人工调拨(值为0),便于后续统计分析。

2.6 调拨单明细表(bb_transfer_order_item)

调拨单明细表记录每张调拨单中具体调拨的商品和数量。

CREATE TABLE bb_transfer_order_item (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  transfer_order_id BIGINT NOT NULL COMMENT '调拨单ID',
  product_id BIGINT NOT NULL COMMENT '商品ID',
  transfer_quantity DECIMAL(18,2) NOT NULL COMMENT '调拨数量',
  actual_quantity DECIMAL(18,2) DEFAULT 0 COMMENT '实际到货数量',
  remark VARCHAR(255) DEFAULT NULL COMMENT '备注'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='调拨单明细表';

2.7 库存操作日志表(bb_inventory_log)

用于追踪所有库存变动操作,确保库存数据的完整性和可追溯性。

CREATE TABLE bb_inventory_log (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
  warehouse_id BIGINT NOT NULL COMMENT '仓库ID',
  product_id BIGINT NOT NULL COMMENT '商品ID',
  change_type VARCHAR(50) NOT NULL COMMENT '变动类型(IN:入库,OUT:出库,TRANSFER_OUT:调拨出库,TRANSFER_IN:调拨入库)',
  quantity_change DECIMAL(18,2) NOT NULL COMMENT '变动数量(正为增加,负为减少)',
  reference_no VARCHAR(50) DEFAULT NULL COMMENT '关联单据号(如调拨单号)',
  created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  remark VARCHAR(255) DEFAULT NULL COMMENT '备注'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存操作日志表';

设计说明:库存操作日志表采用"仅插入"设计模式,所有库存变动都记录为一条新记录,通过 change_type 区分操作类型,便于后续审计和分析。

2.8 表关系说明

各表之间的关系如下图所示:

bb_warehouse (仓库)          bb_product (商品)
      |                            |
      | 1:N                        | 1:N
      v                            v
bb_inventory (库存) <------- bb_sales_record (销售记录)
      |
      |
      v
bb_transfer_order (调拨单主表) ----> bb_transfer_order_item (调拨单明细)
      |
      | (触发记录)
      v
bb_inventory_log (库存操作日志)

三、数据初始化

3.1 仓库样例数据

创建3个区域仓库,分别位于华北、华东和华南:

INSERT INTO bb_warehouse (warehouse_code, warehouse_name, location, manager)
VALUES
('WH001', '华北仓', '北京', '张三'),
('WH002', '华东仓', '上海', '李四'),
('WH003', '华南仓', '广州', '王五');

3.2 商品样例数据

创建5种不同类别的商品,涵盖电子产品、家具和外设:

INSERT INTO bb_product (product_code, product_name, spec, unit, category)
VALUES
('P001', '智能手机', '6.5英寸 128G', '台', '电子产品'),
('P002', '蓝牙耳机', '入耳式', '副', '电子产品'),
('P003', '办公椅', '可调节靠背', '张', '家具'),
('P004', '显示器', '27寸 IPS', '台', '电子产品'),
('P005', '机械键盘', '104键 RGB', '个', '外设');

3.3 库存初始数据

为每个仓库的每种商品初始化库存,数量在100到300之间随机生成:

INSERT INTO bb_inventory (warehouse_id, product_id, quantity, locked_quantity)
SELECT w.id, p.id, FLOOR(RAND() * 200 + 100), 0
FROM bb_warehouse w, bb_product p;

说明FLOOR(RAND() * 200 + 100) 生成100到300之间的随机整数,模拟各仓库初始库存不平衡的场景。

3.4 历史销售数据

生成2023年至2025年的季度销售数据,为AI分析提供历史依据:

-- 创建过去三年的季度销售数据(2023-2025)
SET @start_date = '2023-01-01';
SET @end_date = '2025-12-31';

INSERT INTO bb_sales_record (warehouse_id, product_id, sale_date, quantity, revenue)
SELECT *
FROM (
  SELECT
    w.id AS warehouse_id,
    p.id AS product_id,
    DATE_ADD('2023-01-01', INTERVAL (FLOOR(RAND()*12*3)) MONTH) AS sale_date,
    ROUND(RAND()*500 + 50, 2) AS quantity,
    ROUND(RAND()*50000 + 5000, 2) AS revenue
  FROM bb_warehouse w, bb_product p, (SELECT 1 FROM dual UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) q
) t
WHERE t.sale_date BETWEEN @start_date AND @end_date;

说明:通过交叉连接(Cross Join)生成每个仓库每种商品的多条销售记录,RAND() 函数模拟不同的销售数量和金额,使数据更接近真实业务场景。


四、工程搭建

4.1 项目结构概览

项目采用Maven多模块结构,ai-agent 作为父模块,统一管理依赖版本,ai-inventory-transfer 作为子模块实现具体的库存调拨业务。

ai-agent/                               <-- 父模块(根目录)
├── pom.xml                             <-- 父POM文件
└── ai-inventory-transfer/              <-- 库存调拨子模块
    ├── pom.xml                         <-- 子模块POM文件
    ├── src/
    │   ├── main/
    │   │   ├── java/com/carl/ai/inventory/
    │   │   │   ├── AiInventoryTransferApplication.java
    │   │   │   ├── config/             <-- 配置类
    │   │   │   ├── controller/         <-- 控制器层
    │   │   │   ├── service/            <-- 业务逻辑层
    │   │   │   ├── entity/             <-- 实体类
       │   │   │   ├── mapper/             <-- 数据访问层
    │   │   │   ├── transfer/           <-- 调拨业务模块
    │   │   │   └── utils/              <-- 工具类
    │   │   └── resources/
    │   │       ├── application.yml     <-- 配置文件
    │   │       ├── logback-spring.xml  <-- 日志配置
    │   │       ├── templates/          <-- Thymeleaf模板
    │   │       └── mapper/             <-- MyBatis映射文件
    │   └── test/
    └── ...

4.2 父POM文件

父POM负责统一管理依赖版本和公共依赖,确保子模块版本一致性。

<?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">
    <modelVersion>4.0.0</modelVersion>

    <!-- Spring Boot 父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>3.3.4</version>

        <relativePath/>
    </parent>

    <groupId>com.carl</groupId>

    <artifactId>ai-agent</artifactId>

    <version>1.0-SNAPSHOT</version>

    <packaging>pom</packaging>

    <name>ai-agent</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <java.version>17</java.version>

        <mapstruct.version>1.6.2</mapstruct.version>

        <lombok.version>1.18.30</lombok.version>

        <hutool-all.version>5.8.23</hutool-all.version>

        <spring-ai.version>1.0.3</spring-ai.version>

        <spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>

    </properties>

    <!-- 公共依赖,所有子模块自动继承 -->
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

            <version>${lombok.version}</version>

        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>

            <artifactId>hutool-all</artifactId>

            <version>${hutool-all.version}</version>

        </dependency>

        <dependency>
            <groupId>org.mapstruct</groupId>

            <artifactId>mapstruct</artifactId>

            <version>${mapstruct.version}</version>

        </dependency>

        <dependency>
            <groupId>org.mapstruct</groupId>

            <artifactId>mapstruct-processor</artifactId>

            <version>${mapstruct.version}</version>

        </dependency>

    </dependencies>

    <!-- 依赖版本管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>

                <artifactId>spring-ai-alibaba-bom</artifactId>

                <version>${spring-ai-alibaba.version}</version>

                <type>pom</type>

                <scope>import</scope>

            </dependency>

            <dependency>
                <groupId>org.springframework.ai</groupId>

                <artifactId>spring-ai-bom</artifactId>

                <version>${spring-ai.version}</version>

                <type>pom</type>

                <scope>import</scope>

            </dependency>

        </dependencies>

    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>

                            <artifactId>lombok</artifactId>

                            <version>${lombok.version}</version>

                        </path>

                        <path>
                            <groupId>org.mapstruct</groupId>

                            <artifactId>mapstruct-processor</artifactId>

                            <version>${mapstruct.version}</version>

                        </path>

                    </annotationProcessorPaths>

                </configuration>

            </plugin>

        </plugins>

    </build>

</project>

版本说明

  • spring-ai-alibaba.version:Spring AI Alibaba BOM,提供阿里云大模型接入能力
  • spring-ai.version:Spring AI BOM,提供统一的AI编程抽象
  • mapstruct:对象映射工具,用于DTO和Entity之间的转换
  • lombok:简化Java代码,自动生成getter/setter等方法
  • hutool-all:Java工具类库,提供丰富的实用工具方法

4.3 子模块POM文件

子模块 ai-inventory-transfer 依赖父模块,并引入业务所需的特定依赖。

<?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">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.carl</groupId>

        <artifactId>ai-agent</artifactId>

        <version>1.0-SNAPSHOT</version>

    </parent>

    <artifactId>ai-inventory-transfer</artifactId>

    <version>1.0.0</version>

    <packaging>jar</packaging>

    <name>ai-inventory-transfer</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <java.version>17</java.version>

        <mysql.version>8.0.32</mysql.version>

        <mybatis-plus.verion>3.5.14</mybatis-plus.verion>

        <redisson-verion>3.23.4</redisson-verion>

    </properties>

    <dependencies>
        <!-- Web启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <!-- MyBatis Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>

            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>

            <version>${mybatis-plus.verion}</version>

        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>${mysql.version}</version>

        </dependency>

        <!-- Redis (Redisson) -->
        <dependency>
            <groupId>org.redisson</groupId>

            <artifactId>redisson-spring-boot-starter</artifactId>

            <version>${redisson-verion}</version>

        </dependency>

        <!-- 邮件服务 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-mail</artifactId>

        </dependency>

        <!-- Thymeleaf模板引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-thymeleaf</artifactId>

        </dependency>

        <!-- Spring AI Alibaba Graph Core -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>

            <artifactId>spring-ai-alibaba-graph-core</artifactId>

        </dependency>

        <!-- Spring AI OpenAI自动配置 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>

            <artifactId>spring-ai-autoconfigure-model-openai</artifactId>

        </dependency>

        <!-- Spring AI Chat Client自动配置 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>

            <artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>

        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

    </dependencies>

</project>

依赖说明

  • mybatis-plus-spring-boot3-starter:MyBatis Plus Spring Boot 3 启动器,简化数据访问层开发
  • redisson-spring-boot-starter:Redisson Redis客户端,用于分布式锁和缓存
  • spring-boot-starter-mail:邮件发送功能,用于调拨通知
  • spring-boot-starter-thymeleaf:模板引擎,用于邮件模板渲染
  • spring-ai-alibaba-graph-core:Spring AI Alibaba 图核心库,用于构建AI工作流

五、项目配置

5.1 application.yml

spring:
  application:
    name: ai-inventory-transfer

  # AI 配置:接入阿里云通义千问大模型
  ai:
    openai:
      api-key: sk-e836ecd744a14caca23c9d06b9bd1f1f
      base-url: https://dashscope.aliyuncs.com/compatible-mode
      chat:
        options:
          model: qwen3-max
          temperature: 0.6

  # 数据源配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ai-inventory-transfer?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456

  # Redis配置
  data:
    redis:
      database: 0
      host: localhost
      port: 6379
      timeout: 5s
      connect-timeout: 5s

  # 邮件配置
  mail:
    host: smtp.qq.com
    port: 587
    username: 1525761478@qq.com
    password: efwvvempkglygcjg
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true

  # Thymeleaf模板配置
  thymeleaf:
    cache: true
    check-template: true
    check-template-location: true
    content-type: text/html
    enabled: true
    encoding: UTF-8
    mode: HTML
    prefix: classpath:/templates/
    suffix: .html

# 服务端口
server:
  port: 8876

# 日志配置
logging:
  file:
    path: ./logs
  level:
    root: info
    com.carl: info

# MyBatis Plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置要点

  • temperature: 0.6:控制AI输出的随机性,值越低输出越确定,适合库存分析场景
  • model: qwen3-max:使用阿里云通义千问Max模型,提供强大的分析能力
  • base-url:阿里云DashScope兼容模式接口地址

六、基础框架封装

6.1 统一响应体(R类)

统一响应体用于封装API接口的返回结果,确保前后端交互的数据格式一致性。

package com.carl.ai.inventory.utils;

import lombok.Data;

import java.io.Serializable;

/**
 * 统一响应体
 * @param <T> 数据类型
 */
@Data
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 响应码:200成功,其他为失败
     */
    private Integer code;

    /**
     * 响应消息
     */
    private String msg;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 时间戳
     */
    private Long timestamp;

    public R() {
        this.timestamp = System.currentTimeMillis();
    }

    public R(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
        this.timestamp = System.currentTimeMillis();
    }

    /**
     * 成功响应(无数据)
     */
    public static <T> R<T> ok() {
        return new R<>(200, "操作成功", null);
    }

    /**
     * 成功响应(有数据)
     */
    public static <T> R<T> ok(T data) {
        return new R<>(200, "操作成功", data);
    }

    /**
     * 成功响应(自定义消息)
     */
    public static <T> R<T> ok(String msg, T data) {
        return new R<>(200, msg, data);
    }

    /**
     * 失败响应
     */
    public static <T> R<T> error(String msg) {
        return new R<>(500, msg, null);
    }

    /**
     * 失败响应(自定义状态码)
     */
    public static <T> R<T> error(Integer code, String msg) {
        return new R<>(code, msg, null);
    }

    /**
     * 判断是否成功
     */
    public boolean isSuccess() {
        return Integer.valueOf(200).equals(this.code);
    }
}

6.2 业务异常类(BusinessException)

自定义业务异常,用于区分系统异常和业务逻辑异常。

package com.carl.ai.inventory.exception;

import lombok.Getter;

/**
 * 业务异常
 */
@Getter
public class BusinessException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 错误消息
     */
    private String msg;

    public BusinessException(String msg) {
        super(msg);
        this.code = 500;
        this.msg = msg;
    }

    public BusinessException(Integer code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }

    public BusinessException(String msg, Throwable cause) {
        super(msg, cause);
        this.code = 500;
        this.msg = msg;
    }
}

6.3 全局异常处理器(GlobalExceptionHandler)

统一捕获和处理系统中抛出的各类异常,将异常信息封装为统一响应格式返回给前端。

package com.carl.ai.inventory.exception;

import com.carl.ai.inventory.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public R<Void> handleBusinessException(BusinessException e) {
        log.warn("业务异常:{}", e.getMsg());
        return R.error(e.getCode(), e.getMsg());
    }

    /**
     * 处理参数绑定异常
     */
    @ExceptionHandler(BindException.class)
    public R<Void> handleBindException(BindException e) {
        String msg = e.getAllErrors().get(0).getDefaultMessage();
        log.warn("参数校验失败:{}", msg);
        return R.error(400, msg);
    }

    /**
     * 处理其他所有异常
     */
    @ExceptionHandler(Exception.class)
    public R<Void> handleException(Exception e) {
        log.error("系统异常:", e);
        return R.error(500, "系统繁忙,请稍后重试");
    }
}

6.4 日志配置(logback-spring.xml)

日志配置采用分层的输出策略,支持控制台输出、文件输出和JSON格式输出(可选,用于对接ELK等日志系统)。

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">

    <!-- 1. 定义日志文件存放路径 -->
    <property name="LOG_HOME" value="logs"/>
    <property name="APP_NAME" value="inventory-transfer-system"/>

    <!-- 2. 控制台日志输出格式 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>

            <charset>UTF-8</charset>

        </encoder>

    </appender>

    <!-- 3. 文件日志输出 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${APP_NAME}.log</file>

        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>

            <charset>UTF-8</charset>

        </encoder>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天生成一个日志文件,保留30天 -->
            <fileNamePattern>${LOG_HOME}/${APP_NAME}.%d{yyyy-MM-dd}.log</fileNamePattern>

            <maxHistory>30</maxHistory>

        </rollingPolicy>

    </appender>

    <!-- 4. JSON 文件日志(可选,用于 ELK 等日志系统) -->
    <appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${APP_NAME}.json</file>

        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp/>
                <pattern>
                    <pattern>
                        {
                          "level": "%level",
                          "thread": "%thread",
                          "logger": "%logger",
                          "message": "%message",
                          "exception": "%exception"
                        }
                    </pattern>

                </pattern>

            </providers>

        </encoder>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/${APP_NAME}.%d{yyyy-MM-dd}.json</fileNamePattern>

            <maxHistory>7</maxHistory>

        </rollingPolicy>

    </appender>

    <!-- 5. 日志级别控制 -->
    <logger name="org.springframework" level="INFO"/>
    <logger name="org.hibernate" level="WARN"/>
    <logger name="com.zaxxer.hikari" level="WARN"/>
    <logger name="com.carl" level="DEBUG"/>

    <!-- 6. 根日志输出到控制台与文件 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
        <!-- 如需JSON日志,则打开下一行 -->
        <!-- <appender-ref ref="JSON_FILE"/> -->
    </root>

</configuration>

配置说明

  • scan="true":开启自动扫描配置变更,60秒检查一次
  • 控制台输出使用 %-5level 使日志级别左对齐,便于阅读
  • 文件日志按天滚动,保留30天历史记录
  • JSON日志默认关闭,需要对接ELK日志系统时可启用
  • com.carl 包下日志级别设为DEBUG,方便开发调试

七、代码生成器使用说明

本项目使用 MyBatisX 插件自动生成基础的CRUD代码,包括Entity实体类、Mapper接口、Service接口及实现类、Controller控制器。这大大减少了重复的模板代码编写工作,让开发者可以专注于业务逻辑的实现。

7.1 MyBatisX插件安装

  1. 打开 IntelliJ IDEA,进入 File > Settings > Plugins
  2. 在 Marketplace 中搜索 MyBatisX
  3. 点击 Install 安装插件,重启IDEA

7.2 数据库连接配置

  1. 打开 IDEA 右侧的 Database 工具窗口
  2. 点击 + 号,选择 Data Source > MySQL
  3. 填写数据库连接信息:
    • Host: localhost
    • Port: 3306
    • User: root
    • Password: 123456
    • Database: ai-inventory-transfer
  4. 点击 Test Connection 测试连接,确认无误后点击 OK

7.3 代码生成步骤

  1. 在 Database 工具窗口中,展开数据库连接,找到需要生成代码的表(如 bb_warehousebb_product 等)
  2. 选中需要生成代码的表(可多选),右键点击选择 MyBatisX > Generate MyBatis-Plus
  3. 在弹出的配置窗口中填写以下信息:
    • module path: 选择 ai-inventory-transfer 模块路径
    • base package: 填写 com.carl.ai.inventory
    • relative package: 填写 entity(实体类存放包)
    • encoding: 选择 UTF-8
    • template: 选择 MyBatis-Plus 模板
  4. 在选项配置中勾选需要生成的代码类型:
    • 勾选 Entity:生成实体类
    • 勾选 Mapper:生成Mapper接口
    • 勾选 Service:生成Service接口和实现类
    • 勾选 Controller:生成Controller控制器
  5. 点击 Finish,MyBatisX 将自动生成对应的代码文件

7.4 生成后的代码结构

bb_warehouse 表为例,MyBatisX 会生成以下文件:

com/carl/ai/inventory/
├── entity/
│   └── Warehouse.java              <-- 实体类
├── mapper/
│   └── WarehouseMapper.java        <-- Mapper接口
├── service/
│   ├── WarehouseService.java       <-- Service接口
│   └── impl/
│       └── WarehouseServiceImpl.java <-- Service实现
└── controller/
    └── WarehouseController.java    <-- 控制器

7.5 生成后的调整建议

代码生成后,建议根据实际业务需求进行以下调整:

  1. 实体类:检查字段类型是否与业务匹配,可添加 @TableField 注解进行字段映射配置
  2. Controller:统一返回类型为 R<T>,添加必要的参数校验注解(如 @Valid@NotNull
  3. Service:在生成的CRUD方法基础上,添加业务逻辑方法
  4. Mapper:如需复杂查询,可在Mapper接口中添加自定义方法,并在对应的XML文件中编写SQL

八、本章小结

本章完成了智能库存调拨系统的前置准备工作,主要内容包括:

  1. 系统概述:明确了AI智能库存调拨系统的业务场景、功能模块和核心价值
  2. 表结构设计:创建了6张核心表(仓库、商品、销售记录、库存、调拨单主表、调拨单明细、库存操作日志),构成了完整的数据模型
  3. 数据初始化:通过SQL脚本插入了仓库、商品、库存和历史销售数据,为后续AI分析提供了数据基础
  4. 工程搭建:使用Maven多模块结构搭建了项目框架,父POM统一管理依赖版本,子POM引入业务所需依赖
  5. 项目配置:配置了AI大模型(通义千问)、MySQL数据库、Redis、邮件服务和Thymeleaf模板引擎
  6. 基础框架封装:实现了统一响应体(R类)、业务异常类、全局异常处理器和日志配置
  7. 代码生成:介绍了使用MyBatisX插件自动生成基础CRUD代码的方法和步骤

完成上述准备工作后,下一章将开始实现AI智能调拨建议的核心业务逻辑。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐