skills/xu-cell/ai-engineering-init/leniu-architecture-design

leniu-architecture-design

SKILL.md

leniu-tengyun-core 架构设计指南

双库架构

物理分离双库,Entity 无 tenant_id 字段

库类型 数据范围 访问方式
系统库 租户信息、商户配置、系统字典 Executors.doInSystem()
商户库 订单、菜品、用户、设备等 默认(请求头 MERCHANT-ID

多租户上下文切换

// 获取当前租户
Long tenantId = TenantContextHolder.getTenantId();

// 在指定商户库执行
Executors.doInTenant(tenantId, () -> { /* 商户库 */ });

// 在系统库执行
Executors.doInSystem(() -> { /* 系统库 */ });

// 遍历所有租户执行(定时任务场景)
Executors.doInAllTenant(tenantId -> { /* 每个租户 */ });

双库路由场景

// 场景1:商户业务(默认,前端请求头携带 MERCHANT-ID)
@PostMapping("/order/add")
public void addOrder(@RequestBody LeRequest<OrderDTO> request) {
    orderService.add(request.getContent()); // 自动路由商户库
}

// 场景2:系统配置操作
@PostMapping("/merchant/config")
public void updateConfig(@RequestBody LeRequest<ConfigDTO> request) {
    Executors.doInSystem(() -> configService.update(request.getContent()));
}

// 场景3:定时任务遍历所有租户
public void execute() {
    Executors.doInAllTenant(tenantId -> reportService.generate(tenantId));
}

// 场景4:MQ 消费指定租户
@RabbitListener(queues = "order.queue")
public void consumeOrder(Message message) {
    Long tenantId = getTenantFromMessage(message);
    Executors.doInTenant(tenantId, () -> orderService.process(message));
}

四层架构

Controller → Business → Service → Mapper
职责 命名
Controller 接收请求、参数校验、路由分端 XxxWebController
Business 业务编排、跨 Service 协调 XxxWebBusiness
Service 单表 CRUD、事务 XxxServiceImpl
Mapper ORM 映射(XML 同目录) XxxMapper

Business 层是本项目核心特色,复杂逻辑在此编排,简单 CRUD 可跳过直接 Controller→Service。

Controller 示例

package net.xnzn.core.xxx.controller;

import com.pig4cloud.pigx.common.core.util.LeRequest;
import net.xnzn.framework.secure.filter.annotation.RequiresAuthentication;

@RestController
@RequiresAuthentication
@RequestMapping("/api/v2/web/xxx")
public class XxxWebController {

    @Autowired
    private XxxWebBusiness xxxBusiness;

    @PostMapping("/add")
    public void add(@Validated @RequestBody LeRequest<XxxDTO> request) {
        xxxBusiness.add(request.getContent());
    }

    @PostMapping("/query")
    public Page<XxxVO> query(@Validated @RequestBody LeRequest<PageDTO> request) {
        return xxxBusiness.query(request.getContent());
    }
}

Business 示例

package net.xnzn.core.xxx.business.impl;

@Component
public class XxxWebBusiness {

    @Autowired
    private XxxServiceImpl xxxService;
    @Autowired
    private YyyServiceImpl yyyService;

    public void add(XxxDTO dto) {
        // 编排多个 Service
        xxxService.add(dto);
        yyyService.bindRelation(dto.getId());
    }

    public Page<XxxVO> query(PageDTO dto) {
        return xxxService.query(dto);
    }
}

Service 示例

package net.xnzn.core.xxx.service.impl;

@Service
public class XxxServiceImpl {
    @Autowired
    private XxxMapper xxxMapper;

    public void add(XxxDTO dto) {
        long id = Id.next();
        XxxEntity entity = new XxxEntity();
        BeanUtil.copyProperties(dto, entity);
        entity.setId(id);
        xxxMapper.insert(entity);
    }

    public Page<XxxVO> query(PageDTO dto) {
        return xxxMapper.page(dto.getMpPage(), dto.getKeyword());
    }
}

Mapper 示例

package net.xnzn.core.xxx.mapper;

@Mapper
public interface XxxMapper extends BaseMapper<XxxEntity> {
    Page<XxxVO> page(Page<Object> page, @Param("keyword") String keyword);
}

Mapper XML(与 Java 同目录)

src/main/java/net/xnzn/core/xxx/mapper/
├── XxxMapper.java
└── XxxMapper.xml    ← 同目录,非 resources/mapper/

pom.xml 必须配置资源过滤:

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
</build>

XML 文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="net.xnzn.core.xxx.mapper.XxxMapper">
    <select id="page" resultType="net.xnzn.core.xxx.vo.XxxVO">
        SELECT id, name, status, crtime, uptime
        FROM xxx_table
        WHERE del_flag = 2
        <if test="keyword != null and keyword != ''">
            AND name LIKE CONCAT('%', #{keyword}, '%')
        </if>
    </select>
</mapper>

多端 Controller 路由

路由前缀 Controller 命名
Web 管理端 /api/v2/web/{module} XxxWebController
移动端 /api/v2/mobile/{module} XxxMobileController
设备端 /api/v2/android/{module} XxxAndroidController
开放接口 /api/v2/open/{module} XxxOpenController

Entity 设计

package net.xnzn.core.xxx.model;

@Data
@TableName("xxx_table")
public class XxxEntity {

    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;  // 雪花ID:Id.next()

    private String name;
    private Integer status;

    // 审计字段
    @TableField(fill = FieldFill.INSERT)
    private String crby;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime crtime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String upby;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime uptime;

    // 逻辑删除:1=删除,2=正常
    private Integer delFlag;
}

DelFlagEnum

public enum DelFlagEnum {
    DEL_TRUE(1, "删除"),
    DEL_FALSE(2, "正常");

    private final Integer key;
    private final String val;
}

对应建表 SQL

CREATE TABLE xxx_table (
    id       BIGINT      NOT NULL COMMENT '主键(雪花ID)',
    name     VARCHAR(100) COMMENT '名称',
    status   INT          COMMENT '状态',
    crby     VARCHAR(64)  COMMENT '创建人',
    crtime   DATETIME     COMMENT '创建时间',
    upby     VARCHAR(64)  COMMENT '更新人',
    uptime   DATETIME     COMMENT '更新时间',
    del_flag INT DEFAULT 2 COMMENT '删除标识(1-删除,2-正常)',
    PRIMARY KEY (id)
);
-- 无需 tenant_id(双库物理隔离)

请求响应封装

// 请求体:LeRequest<T> 包装
@PostMapping("/add")
public void add(@Validated @RequestBody LeRequest<XxxDTO> request) {
    XxxDTO dto = request.getContent();
}

// 分页请求
@PostMapping("/query")
public Page<XxxVO> query(@Validated @RequestBody LeRequest<PageDTO> request) {
    return xxxService.query(request.getContent());
}

PageDTO

@Data
public class PageDTO {
    @NotNull @Min(1)
    private Integer pageNum;
    @NotNull @Min(1)
    private Integer pageSize;
    private String keyword;

    public Page<Object> getMpPage() {
        return new Page<>(pageNum, pageSize);
    }
}

异常与国际化

// 业务异常
throw new LeException("业务错误信息");

// 带国际化
throw new LeException(I18n.getMessage("error.key"));

项目模块结构

leniu-tengyun-core/
├── core-base/          # 公共基础模块
├── sys-common/         # 公共业务(支付、通知、对接)
├── sys-canteen/        # 食堂业务
├── sys-kitchen/        # 后场厨房
├── sys-drp/            # 供应链
├── sys-logistics/      # 物流
└── sys-open/           # 开放接口

标准包结构

net.xnzn.core.[module]/
├── controller/         # 按端分:web/mobile/android
├── business/impl/      # 业务编排
├── service/impl/       # 服务层
├── mapper/             # Mapper + XML(同目录)
├── model/              # Entity
├── vo/                 # 响应对象
├── dto/                # 请求参数
├── constants/          # 枚举和常量
├── mq/                 # 消息队列
└── task/               # 定时任务

参考代码位置

类型 路径
订单 Controller sys-canteen/.../order/web/controller/OrderInfoWebController.java
订单 Business sys-canteen/.../order/web/business/impl/OrderWebBusiness.java
订单 Service sys-canteen/.../order/common/service/impl/OrderInfoService.java
排班 Controller sys-kitchen/.../attendance/scheduling/controller/BackAttendanceWorkShiftController.java
Bootstrap 配置 core-base/src/main/resources/bootstrap.yml
Weekly Installs
4
GitHub Stars
8
First Seen
8 days ago
Installed on
gemini-cli4
github-copilot4
codex4
kimi-cli4
cursor4
amp4