You need to sign in or sign up before continuing.
技术架构设计.md 37.6 KB

Apple经销商ERP系统对接项目 - 技术架构设计

1. 项目概述

1.1 项目背景

Apple经销商ERP系统对接项目旨在构建全链路返利监控体系,实现下游渠道精细化管理。系统通过标准化接口实时获取经销商ERP关键业务数据,包括订单基础信息、库存流转状态、财务结算凭证等,同时追踪每笔订单返利使用明细,形成"订单-出库-开票-返利使用"全流程数据闭环。

1.2 核心目标

  • 构建全链路返利监控体系:精准掌握返利政策落地执行情况
  • 实现下游渠道精细化管理:通过系统对接实现与经销商ERP平台的业务数据联通
  • 建立多维度校验机制:防范数据篡改或异常申报风险
  • 提供可靠数据支撑:为返利政策落地与渠道决策提供依据

1.3 技术选型原则

  • 参考若依架构:借鉴若依成熟的技术架构和设计模式
  • 从0开始开发:基于若依架构思想,重新设计和实现
  • 技术栈现代化:选择主流、稳定的技术栈
  • 可扩展性强:支持业务快速发展和功能扩展

2. 整体架构设计

2.1 系统架构图

┌─────────────────────────────────────────────────────────────────┐
│                        用户层 (User Layer)                        │
├─────────────────────────────────────────────────────────────────┤
│  Web浏览器  │  第三方系统  │  经销商ERP系统  │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     应用层 (Application Layer)                    │
├─────────────────────────────────────────────────────────────────┤
│  订单管理服务  │  出库查询服务  │  发票管理服务  │  返利看板服务  │
│  数据验证服务  │  异常工单服务  │  产品管理服务  │  经销商管理服务 │
│  系统管理服务  │  首页统计服务  │  文件管理服务  │  消息通知服务  │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     业务层 (Business Layer)                       │
├─────────────────────────────────────────────────────────────────┤
│  数据验证引擎  │  返利计算引擎  │  异常处理引擎  │  报表生成引擎  │
│  权限控制引擎  │  工作流引擎    │  缓存管理引擎  │  日志记录引擎  │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     数据层 (Data Layer)                          │
├─────────────────────────────────────────────────────────────────┤
│  MySQL数据库  │  Redis缓存  │
└─────────────────────────────────────────────────────────────────┘

2.2 技术架构分层

2.2.1 表现层 (Presentation Layer)

  • 前端技术栈:Vue 3 + TypeScript + Element Plus
  • 构建工具:Vite
  • 状态管理:Pinia
  • 路由管理:Vue Router 4
  • HTTP客户端:Axios
  • 图表组件:ECharts 5
  • UI组件库:Element Plus
  • 目标平台:Web浏览器(PC端)

2.2.2 控制层 (Controller Layer)

  • Web框架:Spring Boot 2.7.x
  • MVC框架:Spring MVC
  • 参数校验:Spring Validation
  • 异常处理:全局异常处理器
  • 跨域处理:CORS配置

2.2.3 业务层 (Service Layer)

  • 业务逻辑:Spring Service
  • 事务管理:Spring Transaction
  • 缓存管理:Spring Cache + Redis
  • 异步处理:Spring Async
  • 定时任务:Spring Task

2.2.4 数据访问层 (Data Access Layer)

  • ORM框架:MyBatis Plus 3.5.x
  • 数据库连接池:HikariCP
  • 分页插件:MyBatis Plus分页插件
  • 代码生成器:MyBatis Plus代码生成器

2.2.5 数据存储层 (Data Storage Layer)

  • 关系型数据库:MySQL 8.0
  • 缓存数据库:Redis 6.0

3. 技术栈详细设计

3.1 后端技术栈

3.1.1 核心框架

<!-- Spring Boot 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.18</version>
</dependency>

<!-- Spring Security 安全框架 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.7.18</version>
</dependency>

<!-- Spring Data Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.7.18</version>
</dependency>

3.1.2 数据访问层

<!-- MyBatis Plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>

<!-- HikariCP 连接池 -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
</dependency>

3.1.3 工具类库

<!-- Hutool 工具类 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.20</version>
</dependency>

<!-- Jackson JSON处理 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

<!-- Apache Commons -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

3.2 前端技术栈

3.2.1 核心框架

{
  "dependencies": {
    "vue": "^3.3.4",
    "vue-router": "^4.2.4",
    "pinia": "^2.1.6",
    "element-plus": "^2.3.8",
    "axios": "^1.4.0",
    "echarts": "^5.4.2"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.3",
    "vite": "^4.4.5",
    "typescript": "^5.1.6",
    "@types/node": "^20.4.5"
  }
}

3.2.2 项目结构

src/
├── api/                 # API接口定义
├── assets/             # 静态资源
├── components/         # 公共组件
├── layouts/            # 布局组件(PC端布局)
├── router/             # 路由配置
├── stores/             # 状态管理
├── styles/             # 样式文件
├── utils/              # 工具函数
├── views/              # 页面组件
│   ├── dashboard/      # 首页
│   ├── order/          # 订单管理
│   ├── delivery/       # 出库查询
│   ├── invoice/        # 发票管理
│   ├── rebate/         # 返利看板
│   ├── validation/     # 数据验证
│   ├── workorder/      # 异常工单
│   ├── product/        # 产品管理
│   ├── dealer/         # 经销商管理
│   └── system/         # 系统管理
└── main.ts             # 入口文件

4. 核心模块设计

4.1 订单管理模块

4.1.1 模块架构

@RestController
@RequestMapping("/api/order")
@Api(tags = "订单管理")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("/list")
    @ApiOperation("订单查询")
    public R<PageResult<OrderVO>> list(OrderQueryDTO query) {
        return R.ok(orderService.selectOrderList(query));
    }

    @PostMapping("/export")
    @ApiOperation("订单导出")
    public void export(OrderQueryDTO query, HttpServletResponse response) {
        orderService.exportOrder(query, response);
    }
}

4.1.2 业务服务层

@Service
@Transactional
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Override
    public PageResult<OrderVO> selectOrderList(OrderQueryDTO query) {
        Page<Order> page = new Page<>(query.getPageNum(), query.getPageSize());
        List<Order> orders = orderMapper.selectOrderList(page, query);
        List<OrderVO> orderVOs = BeanUtil.copyToList(orders, OrderVO.class);
        return new PageResult<>(orderVOs, page.getTotal());
    }
}

4.2 数据验证模块

4.2.1 验证引擎设计

@Component
public class DataValidationEngine {

    @Autowired
    private List<ValidationRule> validationRules;

    /**
     * 执行数据验证
     */
    public ValidationResult validate(ValidationContext context) {
        ValidationResult result = new ValidationResult();

        for (ValidationRule rule : validationRules) {
            if (rule.supports(context)) {
                ValidationResult ruleResult = rule.validate(context);
                result.merge(ruleResult);
            }
        }

        return result;
    }
}

4.2.2 验证规则实现

@Component
public class TimeSequenceValidationRule implements ValidationRule {

    @Override
    public boolean supports(ValidationContext context) {
        return context.getDataType() == DataType.ORDER;
    }

    @Override
    public ValidationResult validate(ValidationContext context) {
        ValidationResult result = new ValidationResult();
        OrderData orderData = context.getOrderData();

        // 验证开票时间不得早于出库时间
        if (orderData.getInvoiceDate() != null && 
            orderData.getDeliveryDate() != null &&
            orderData.getInvoiceDate().before(orderData.getDeliveryDate())) {
            result.addError("开票时间不得早于出库时间");
        }

        return result;
    }
}

4.3 异常工单模块

4.3.1 工单服务设计

@Service
@Transactional
public class WorkorderServiceImpl implements WorkorderService {

    @Autowired
    private WorkorderMapper workorderMapper;

    @Autowired
    private WorkorderLogMapper workorderLogMapper;

    @Autowired
    private NotificationService notificationService;

    /**
     * 创建异常工单
     */
    @Override
    public void createWorkorder(ValidationResult validationResult) {
        Workorder workorder = new Workorder();
        workorder.setWorkorderNo(generateWorkorderNo());
        workorder.setExceptionType(validationResult.getExceptionType());
        workorder.setSeverityLevel(validationResult.getSeverityLevel());
        workorder.setExceptionDesc(validationResult.getErrorMessage());
        workorder.setWorkorderStatus(WorkorderStatus.PENDING);

        workorderMapper.insert(workorder);

        // 发送通知
        if (workorder.getSeverityLevel() == SeverityLevel.HIGH) {
            notificationService.sendHighPriorityNotification(workorder);
        }
    }
}

4.4 返利计算模块

4.4.1 返利计算引擎

@Component
public class RebateCalculationEngine {

    @Autowired
    private RebateRuleService rebateRuleService;

    @Autowired
    private ProductService productService;

    /**
     * 计算订单返利
     */
    public BigDecimal calculateOrderRebate(OrderData orderData) {
        BigDecimal totalRebate = BigDecimal.ZERO;

        for (OrderItemData item : orderData.getItems()) {
            ProductInfo product = productService.getByCode(item.getProductCode());
            if (product.getRebateFlag() == 1) {
                RebateRule rule = rebateRuleService.getRule(product, orderData.getDealerLevel());
                BigDecimal itemRebate = rule.calculateRebate(item);
                totalRebate = totalRebate.add(itemRebate);
            }
        }

        return totalRebate;
    }
}

5. 数据库设计

5.1 数据库架构

-- 创建数据库
CREATE DATABASE apple_erp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE apple_erp;

5.2 核心表结构

5.2.1 订单主表

CREATE TABLE t_order_main (
    order_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
    order_no VARCHAR(30) NOT NULL UNIQUE COMMENT '订单编号',
    dealer_code VARCHAR(20) NOT NULL COMMENT '经销商编码',
    dealer_name VARCHAR(100) NOT NULL COMMENT '经销商名称',
    order_date DATETIME NOT NULL COMMENT '订单创建日期',
    total_amount DECIMAL(18,2) NOT NULL COMMENT '订单总金额',
    rebate_amount DECIMAL(18,2) DEFAULT 0 COMMENT '订单返利金额',
    delivery_status TINYINT DEFAULT 0 COMMENT '出库状态',
    invoice_status TINYINT DEFAULT 0 COMMENT '开票状态',
    rebate_calc_flag TINYINT DEFAULT 0 COMMENT '返利计算状态',
    data_source VARCHAR(20) COMMENT '数据来源',
    verify_status TINYINT DEFAULT 0 COMMENT '数据验证状态',
    upload_time DATETIME COMMENT '数据上传时间',
    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单主表';

5.2.2 系统用户表

CREATE TABLE t_sys_user (
    user_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
    username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
    real_name VARCHAR(50) NOT NULL COMMENT '真实姓名',
    password VARCHAR(100) NOT NULL COMMENT '密码',
    phone VARCHAR(20) COMMENT '手机号',
    email VARCHAR(100) COMMENT '邮箱',
    status TINYINT DEFAULT 1 COMMENT '状态',
    last_login_time DATETIME COMMENT '最后登录时间',
    last_login_ip VARCHAR(50) COMMENT '最后登录IP',
    remark VARCHAR(500) COMMENT '备注',
    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统用户表';

6. 安全架构设计

6.1 认证授权

6.1.1 Spring Security配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .antMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and()
            .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

6.1.2 JWT Token管理

@Component
public class JwtTokenProvider {

    private static final String SECRET_KEY = "apple-erp-secret-key";
    private static final long EXPIRATION_TIME = 86400000; // 24小时

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", userDetails.getUsername());
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }
}

6.2 数据安全

6.2.1 敏感数据加密

@Component
public class DataEncryption {

    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final String SECRET_KEY = "AppleERP2025Key";

    /**
     * 加密敏感数据
     */
    public String encrypt(String data) {
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            byte[] encrypted = cipher.doFinal(data.getBytes());
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new RuntimeException("数据加密失败", e);
        }
    }

    /**
     * 解密敏感数据
     */
    public String decrypt(String encryptedData) {
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] decoded = Base64.getDecoder().decode(encryptedData);
            byte[] decrypted = cipher.doFinal(decoded);
            return new String(decrypted);
        } catch (Exception e) {
            throw new RuntimeException("数据解密失败", e);
        }
    }
}

7. 缓存架构设计

7.1 Redis配置

7.1.1 Redis配置类

@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();

        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

7.1.2 缓存策略

@Service
public class CacheService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 经销商信息缓存
     */
    @Cacheable(value = "dealer", key = "#dealerCode")
    public DealerInfo getDealerInfo(String dealerCode) {
        return dealerMapper.selectByCode(dealerCode);
    }

    /**
     * 产品信息缓存
     */
    @Cacheable(value = "product", key = "#productCode")
    public ProductInfo getProductInfo(String productCode) {
        return productMapper.selectByCode(productCode);
    }

    /**
     * 字典数据缓存
     */
    @Cacheable(value = "dict", key = "#dictType")
    public List<DictItem> getDictItems(String dictType) {
        return dictMapper.selectByType(dictType);
    }
}

8. 接口设计规范

8.1 RESTful API设计

8.1.1 统一返回格式

@Data
@ApiModel("统一返回结果")
public class R<T> {

    @ApiModelProperty("状态码")
    private Integer code;

    @ApiModelProperty("返回消息")
    private String msg;

    @ApiModelProperty("数据对象")
    private T data;

    public static <T> R<T> ok() {
        return restResult(null, 200, "操作成功");
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, 200, "操作成功");
    }

    public static <T> R<T> error(String msg) {
        return restResult(null, 500, msg);
    }

    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }
}

8.1.2 分页查询结果

@Data
@ApiModel("分页查询结果")
public class PageResult<T> {

    @ApiModelProperty("数据列表")
    private List<T> records;

    @ApiModelProperty("总记录数")
    private Long total;

    @ApiModelProperty("当前页码")
    private Long current;

    @ApiModelProperty("每页大小")
    private Long size;

    public PageResult(List<T> records, Long total) {
        this.records = records;
        this.total = total;
    }
}

8.2 接口文档

8.2.1 Swagger配置

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.apple.erp.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Apple经销商ERP系统API文档")
                .description("Apple经销商ERP系统对接项目接口文档")
                .version("1.0.0")
                .build();
    }
}

9. 异常处理设计

9.1 全局异常处理

9.1.1 异常处理器

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public R<Void> handleServiceException(ServiceException e) {
        log.error("业务异常:{}", e.getMessage());
        return R.error(e.getMessage());
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<Void> handleValidationException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        log.error("参数校验异常:{}", message);
        return R.error("参数错误: " + message);
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public R<Void> handleException(Exception e) {
        log.error("系统异常", e);
        return R.error("系统异常,请联系管理员");
    }
}

9.1.2 自定义异常

public class ServiceException extends RuntimeException {

    private Integer code;

    public ServiceException(String message) {
        super(message);
        this.code = 500;
    }

    public ServiceException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public Integer getCode() {
        return code;
    }
}

10. 日志设计

10.1 日志配置

10.1.1 Logback配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 文件输出 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/apple-erp.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/apple-erp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE"/>
        <queueSize>1024</queueSize>
        <discardingThreshold>0</discardingThreshold>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="ASYNC"/>
    </root>
</configuration>

10.1.2 操作日志切面

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private SysLogService sysLogService;

    @Around("@annotation(log)")
    public Object around(ProceedingJoinPoint point, Log log) throws Throwable {
        long startTime = System.currentTimeMillis();

        try {
            Object result = point.proceed();
            long endTime = System.currentTimeMillis();

            // 记录操作日志
            saveLog(point, log, endTime - startTime, null);

            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            saveLog(point, log, endTime - startTime, e);
            throw e;
        }
    }

    private void saveLog(ProceedingJoinPoint point, Log log, long time, Exception e) {
        SysLog sysLog = new SysLog();
        sysLog.setModule(log.module());
        sysLog.setOperation(log.operation());
        sysLog.setLogTime(new Date());
        sysLog.setStatus(e == null ? 1 : 0);
        sysLog.setErrorMsg(e != null ? e.getMessage() : null);

        sysLogService.save(sysLog);
    }
}

11. 性能优化设计

11.1 数据库优化

11.1.1 索引设计

-- 订单表索引
CREATE INDEX idx_order_dealer_code ON t_order_main(dealer_code);
CREATE INDEX idx_order_date ON t_order_main(order_date);
CREATE INDEX idx_order_status ON t_order_main(delivery_status, invoice_status);

-- 出库表索引
CREATE INDEX idx_delivery_order_no ON t_delivery_main(order_no);
CREATE INDEX idx_delivery_date ON t_delivery_main(delivery_date);

-- 发票表索引
CREATE INDEX idx_invoice_order_no ON t_invoice_main(order_no);
CREATE INDEX idx_invoice_date ON t_invoice_main(invoice_date);

11.1.2 查询优化

@Service
public class OrderServiceImpl implements OrderService {

    /**
     * 分页查询优化
     */
    @Override
    public PageResult<OrderVO> selectOrderList(OrderQueryDTO query) {
        // 使用覆盖索引优化查询
        Page<Order> page = new Page<>(query.getPageNum(), query.getPageSize());

        // 构建查询条件
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(query.getDealerCode()), Order::getDealerCode, query.getDealerCode())
               .ge(query.getStartDate() != null, Order::getOrderDate, query.getStartDate())
               .le(query.getEndDate() != null, Order::getOrderDate, query.getEndDate())
               .eq(query.getDeliveryStatus() != null, Order::getDeliveryStatus, query.getDeliveryStatus())
               .orderByDesc(Order::getOrderDate);

        List<Order> orders = orderMapper.selectList(page, wrapper);
        List<OrderVO> orderVOs = BeanUtil.copyToList(orders, OrderVO.class);

        return new PageResult<>(orderVOs, page.getTotal());
    }
}

11.2 前端优化

11.2.1 组件懒加载

// 路由懒加载
const routes = [
  {
    path: '/order',
    name: 'Order',
    component: () => import('@/views/order/index.vue')
  },
  {
    path: '/delivery',
    name: 'Delivery',
    component: () => import('@/views/delivery/index.vue')
  }
];

11.2.2 虚拟滚动

<template>
  <el-table-v2
    :columns="columns"
    :data="tableData"
    :width="700"
    :height="400"
    fixed
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const columns = ref([
  { key: 'orderNo', title: '订单编号', width: 150 },
  { key: 'dealerName', title: '经销商名称', width: 200 },
  { key: 'totalAmount', title: '订单金额', width: 120 },
  { key: 'orderDate', title: '订单日期', width: 150 }
]);

const tableData = ref([]);
</script>

12. 监控设计

12.1 应用监控

12.1.1 Actuator配置

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always
  metrics:
    export:
      prometheus:
        enabled: true

12.1.2 自定义监控指标

@Component
public class BusinessMetrics {

    private final MeterRegistry meterRegistry;

    public BusinessMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    /**
     * 订单创建计数器
     */
    public void incrementOrderCreated() {
        meterRegistry.counter("order.created").increment();
    }

    /**
     * 异常工单生成计数器
     */
    public void incrementExceptionGenerated() {
        meterRegistry.counter("exception.generated").increment();
    }

    /**
     * 数据验证失败计数器
     */
    public void incrementValidationFailed() {
        meterRegistry.counter("validation.failed").increment();
    }
}

13. 部署架构

13.1 容器化部署

13.1.1 Dockerfile

# 后端应用Dockerfile
FROM openjdk:8-jre-alpine

VOLUME /tmp

COPY target/apple-erp.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
# 前端应用Dockerfile
FROM node:16-alpine as build

WORKDIR /app
COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

13.1.2 Docker Compose

version: '3.8'

services:
  # 后端应用
  app:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - mysql
      - redis
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_HOST=mysql
      - REDIS_HOST=redis
    networks:
      - apple-erp-network

  # 前端应用(可选,开发时可直接访问后端)
  # frontend:
  #   build: ./frontend
  #   ports:
  #     - "80:80"
  #   depends_on:
  #     - app
  #   networks:
  #     - apple-erp-network

  # MySQL数据库
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root123456
      MYSQL_DATABASE: apple_erp
      MYSQL_USER: apple_erp
      MYSQL_PASSWORD: apple_erp123
    volumes:
      - mysql_data:/var/lib/mysql
      - ./sql:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"
    networks:
      - apple-erp-network

  # Redis缓存
  redis:
    image: redis:6.0-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - apple-erp-network

volumes:
  mysql_data:
  redis_data:

networks:
  apple-erp-network:
    driver: bridge

13.2 简化部署配置

13.2.1 开发环境配置

# application-dev.yml
server:
  port: 8080
  servlet:
    context-path: /

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/apple_erp?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

  redis:
    host: localhost
    port: 6379
    password: 
    database: 0
    timeout: 10000ms

13.2.2 生产环境配置

# application-prod.yml
server:
  port: 8080
  servlet:
    context-path: /

spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:apple_erp}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:123456}
    driver-class-name: com.mysql.cj.jdbc.Driver

  redis:
    host: ${REDIS_HOST:localhost}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PASSWORD:}
    database: ${REDIS_DATABASE:0}
    timeout: 10000ms

14. 开发规范

14.1 代码规范

14.1.1 包结构规范

com.apple.erp
├── common/                 # 公共模块
│   ├── config/            # 配置类
│   ├── constant/          # 常量定义
│   ├── enums/             # 枚举类
│   ├── exception/         # 异常类
│   ├── utils/             # 工具类
│   └── result/            # 返回结果类
├── modules/               # 业务模块
│   ├── order/             # 订单管理
│   ├── delivery/          # 出库查询
│   ├── invoice/           # 发票管理
│   ├── rebate/            # 返利看板
│   ├── validation/        # 数据验证
│   ├── workorder/         # 异常工单
│   ├── product/           # 产品管理
│   ├── dealer/            # 经销商管理
│   └── system/            # 系统管理
└── Application.java       # 启动类

14.1.2 命名规范

// 类命名:大驼峰命名法
public class OrderController {}
public class OrderServiceImpl {}

// 方法命名:小驼峰命名法
public void selectOrderList() {}
public void createOrder() {}

// 常量命名:全大写,下划线分隔
public static final String ORDER_STATUS_PENDING = "PENDING";

// 包命名:全小写,点分隔
com.apple.erp.modules.order.controller

14.2 接口规范

14.2.1 Controller规范

@RestController
@RequestMapping("/api/order")
@Api(tags = "订单管理")
@Slf4j
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("/list")
    @ApiOperation("订单查询")
    @PreAuthorize("hasAuthority('order:list')")
    public R<PageResult<OrderVO>> list(OrderQueryDTO query) {
        try {
            PageResult<OrderVO> result = orderService.selectOrderList(query);
            return R.ok(result);
        } catch (Exception e) {
            log.error("订单查询失败", e);
            return R.error("查询失败");
        }
    }
}

15. 总结

15.1 架构特点

  • 参考若依架构:借鉴若依成熟的技术架构和设计模式
  • 从0开始开发:基于若依架构思想,重新设计和实现
  • 模块化设计:清晰的模块划分,便于维护和扩展
  • 标准化开发:统一的开发规范和接口设计

15.2 技术优势

  • 技术栈现代化:使用主流、稳定的技术栈
  • 性能优化:数据库优化、缓存策略、前端优化
  • 安全可靠:完善的认证授权和数据安全机制
  • 监控完善:应用监控、业务监控、日志监控

15.3 扩展性

  • 功能扩展:模块化设计,便于新增功能模块
  • 数据扩展:支持分库分表和读写分离
  • 接口扩展:RESTful API设计,便于第三方集成
  • 架构扩展:后续可引入网关、负载均衡等组件
  • 平台扩展:后续可开发移动端H5、小程序等
  • 存储扩展:后续可引入文件存储、消息队列等组件

文档版本: v1.0
创建时间: 2025-01-27
最后更新: 2025-01-27
维护人员: 开发团队