Skip to content

后端开发概述

作者:唐亚峰 | battcn
字数统计:2.2k 字

学习目标

掌握 Wemirr Platform 后端项目结构、开发规范和核心功能

技术栈

技术版本说明
JDK17/21Java 开发工具包
Spring Boot3.x基础框架
Spring Cloud2024.x微服务框架
Spring Cloud Alibaba2023.x阿里微服务组件
Sa-Token最新版轻量级权限认证框架
MyBatis-Plus3.5.x增强版 MyBatis
Nacos2.x服务注册与配置中心
Redis6+缓存、分布式锁
MySQL8.0关系型数据库

项目结构

wemirr-platform/
├── wemirr-platform-dependencies/       # 📦 依赖版本管理(BOM)
├── wemirr-platform-framework/          # 🔧 框架核心层
│   ├── common-framework-core/          # 基础核心(注解、接口、工具类)
│   ├── common-spring-boot-starter/     # Spring Boot 核心启动器
│   ├── db-spring-boot-starter/         # 数据库增强(MyBatis-Plus、多租户)
│   ├── redis-plus-spring-boot-starter/ # Redis 缓存增强
│   ├── security-spring-boot-starter/   # 安全认证(Sa-Token)
│   ├── feign-plugin-spring-boot-starter/ # Feign 增强
│   ├── easyexcel-spring-boot-starter/  # Excel 导入导出
│   ├── diff-log-spring-boot-starter/   # 差异日志
│   ├── i18n-spring-boot-starter/       # 国际化
│   ├── pdf-spring-boot-starter/        # PDF 生成
│   ├── robot-spring-boot-starter/      # 消息机器人
│   └── websocket-spring-boot-starter/  # 分布式 WebSocket

├── wemirr-platform-feign/              # 🔗 Feign 接口定义
│   ├── wemirr-platform-iam-api/        # IAM 服务 API
│   ├── wemirr-platform-suite-api/      # Suite 服务 API
│   └── wemirr-platform-workflow-api/   # 工作流服务 API

├── wemirr-platform-iam/                # 🔐 IAM 认证授权中心
│   └── src/main/java/.../iam/
│       ├── auth/                       # 认证模块
│       ├── base/                       # 基础模块
│       ├── system/                     # 系统管理
│       └── tenant/                     # 租户管理

├── wemirr-platform-suite/              # 💼 业务套件中心
├── wemirr-platform-gateway/            # 🚪 API 网关

├── wemirr-plugin/                      # 🔌 插件扩展中心
│   ├── wemirr-platform-ai/             # AI 能力(Langchain4j)
│   ├── wemirr-platform-workflow/       # 审批流程(Warm-Flow)
│   ├── wemirr-platform-monitor/        # 监控中心
│   ├── wemirr-platform-tms/            # 运输管理
│   └── wemirr-platform-wms/            # 仓储管理

└── 附件/                                # 📁 配套资源
    ├── docker/                         # Docker 编排
    ├── mysql/                          # 数据库脚本
    ├── nacos/                          # Nacos 配置
    └── nginx/                          # Nginx 配置

模块说明

IAM 服务结构

wemirr-platform-iam 为例,展示服务内部结构:

wemirr-platform-iam/
└── src/main/java/com/wemirr/platform/iam/
    ├── IamApplication.java         # 启动类
    ├── auth/                       # 认证模块
    │   ├── controller/             # 登录、Token 等接口
    │   └── service/
    ├── base/                       # 基础模块
    ├── system/                     # 系统管理
    │   ├── controller/             # 用户、角色、菜单等接口
    │   │   ├── UserController.java
    │   │   ├── RoleController.java
    │   │   ├── ResourceController.java
    │   │   ├── OrgController.java
    │   │   └── PositionController.java
    │   ├── domain/                 # 领域模型
    │   │   ├── entity/             # 实体类
    │   │   ├── dto/                # 请求对象
    │   │   │   ├── req/            # 请求参数
    │   │   │   └── resp/           # 响应结果
    │   │   └── convert/            # 对象转换
    │   ├── repository/             # 数据访问层(Mapper)
    │   ├── service/                # 业务逻辑
    │   └── func/                   # 功能函数
    └── tenant/                     # 租户管理

框架组件说明

组件说明
common-framework-core基础注解、接口、异常、工具类
common-spring-boot-starter全局异常处理、参数校验、日志等
db-spring-boot-starterMyBatis-Plus 配置、多租户、数据权限
redis-plus-spring-boot-starterRedis 配置、分布式锁、缓存注解
security-spring-boot-starterSa-Token 配置、权限注解
feign-plugin-spring-boot-starterFeign 增强、Token 传递、请求头复制
easyexcel-spring-boot-starterExcel 导入导出,支持注解方式
diff-log-spring-boot-starter变更日志记录
robot-spring-boot-starter钉钉、企微、飞书机器人消息
websocket-spring-boot-starter分布式 WebSocket 消息推送

快速开始

1. 创建新服务

bash
# 在 wemirr-platform 目录下创建新模块
mkdir -p wemirr-platform-demo/{demo-api,demo-biz,demo-server}

2. 配置 pom.xml

xml
<!-- demo-server/pom.xml -->
<parent>
    <groupId>com.wemirr.platform</groupId>
    <artifactId>wemirr-platform-demo</artifactId>
    <version>${revision}</version>
</parent>

<artifactId>demo-server</artifactId>

<dependencies>
    <dependency>
        <groupId>com.wemirr.platform</groupId>
        <artifactId>demo-biz</artifactId>
    </dependency>
    <dependency>
        <groupId>com.wemirr.framework</groupId>
        <artifactId>common-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.wemirr.framework</groupId>
        <artifactId>db-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

3. 创建启动类

java
@SpringBootApplication
@EnableDiscoveryClient
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

4. 配置文件

yaml
# bootstrap.yml
spring:
  application:
    name: wemirr-platform-demo
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
        namespace: ${NACOS_NAMESPACE:v4-dev}
      config:
        server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
        namespace: ${NACOS_NAMESPACE:v4-dev}
        file-extension: yml
        shared-configs:
          - data-id: common.yml
            group: DEFAULT_GROUP
            refresh: true

开发规范

代码分层

Controller  →  接收请求、参数校验、调用 Service

Service     →  业务逻辑、事务管理、调用 Mapper

Mapper      →  数据访问、SQL 操作

Entity      →  数据库实体映射

命名规范

类型规范示例
类名PascalCaseUserController
方法名camelCasegetUserById
变量名camelCaseuserName
常量UPPER_SNAKE_CASEMAX_RETRY_COUNT
包名全小写com.wemirr.iam

接口规范

java
// RESTful 风格
GET    /users          // 列表查询
GET    /users/{id}     // 单个查询
POST   /users          // 新增
PUT    /users/{id}     // 更新
DELETE /users/{id}     // 删除

// 统一响应格式
{
  "code": 0,           // 状态码,0 成功,其他失败
  "message": "success",// 消息
  "data": {}           // 数据
}

异常处理

java
// 使用统一业务异常
throw new BizException("用户不存在");
throw new BizException(ResultCode.USER_NOT_FOUND);

// 异常枚举
public enum ResultCode {
    SUCCESS(0, "成功"),
    USER_NOT_FOUND(10001, "用户不存在"),
    PARAM_ERROR(10002, "参数错误"),
    // ...
}

核心功能(真实代码示例)

以下代码均来自 wemirr-platform-iam 模块,是项目中的真实实现。

Controller 示例

java
// 来自 UserController.java
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/users")
@Tag(name = "用户管理", description = "用户管理")
public class UserController {
    
    private final UserService userService;
    
    @PostMapping("/page")
    @Operation(summary = "用户列表")
    @SaCheckPermission(value = {"sys:user:page"})
    public IPage<UserPageResp> pageList(@RequestBody UserPageReq req) {
        return this.userService.pageList(req);
    }
    
    @PostMapping("/create")
    @AccessLog(module = "用户管理", description = "添加用户")
    @Operation(summary = "添加用户")
    @SaCheckPermission(value = {"sys:user:add"})
    public void create(@Validated @RequestBody UserSaveReq req) {
        this.userService.create(req);
    }
    
    @PutMapping("/{id}")
    @AccessLog(module = "用户管理", description = "编辑用户")
    @SaCheckPermission(value = {"sys:user:edit"})
    public void modify(@PathVariable Long id, @Validated @RequestBody UserUpdateReq req) {
        this.userService.modify(id, req);
    }
    
    @DeleteMapping("/{id}")
    @AccessLog(module = "用户管理", description = "删除用户")
    @SaCheckPermission(value = {"sys:user:remove"})
    public void del(@PathVariable Long id) {
        this.userService.delete(id);
    }
    
    @PostMapping("/export")
    @SaCheckPermission(value = {"sys:user:export"})
    @ResponseExcel(fileName = "用户列表")  // 一键导出 Excel
    public List<UserPageResp> exportList(@RequestBody UserPageReq req) {
        req.setCurrent(1);
        req.setSize(-1);
        return this.userService.pageList(req).getRecords();
    }
}

Service 示例

java
// 来自 UserServiceImpl.java
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends SuperServiceImpl<UserMapper, User> implements UserService {

    private final AuthenticationContext context;
    
    @Override
    public void create(UserSaveReq req) {
        // 检查账号是否存在
        final long count = super.count(Wraps.<User>lbQ().eq(User::getUsername, req.getUsername()));
        if (count > 0) {
            throw CheckedException.badRequest("账号已存在");
        }
        var bean = BeanUtil.toBean(req, User.class);
        bean.setPassword(PasswordEncoderHelper.encode(req.getPassword()));
        bean.setTenantId(context.tenantId());  // 自动设置租户ID
        this.baseMapper.insert(bean);
    }
    
    @Override
    @DiffLog(group = "用户管理", tag = "编辑用户", businessKey = "{{#id}}",
            success = "更新用户信息 {_DIFF{#_newObj}}",
            fail = "更新用户信息异常 {{#id}}")
    public void modify(Long id, UserUpdateReq req) {
        User oldUser = Optional.ofNullable(this.baseMapper.selectById(id))
                .orElseThrow(() -> CheckedException.notFound("用户不存在"));
        User newUser = BeanUtilPlus.toBean(id, req, User.class);
        DiffLogContext.putDiffItem(oldUser, newUser);  // 差异日志
        this.baseMapper.updateById(newUser);
    }
    
    @Override
    @DSTransactional(rollbackFor = Exception.class)  // 分布式事务
    public void delete(Long id) {
        final User user = Optional.ofNullable(getById(id))
                .orElseThrow(() -> CheckedException.notFound("用户不存在"));
        if (user.getReadonly()) {
            throw CheckedException.badRequest("内置用户不允许删除");
        }
        baseMapper.deleteById(id);
        userRoleMapper.delete(Wraps.<UserRole>lbQ().eq(UserRole::getUserId, id));
    }
}

请求参数校验

java
// 来自 UserSaveReq.java - 新增用户请求
@Data
@Schema(name = "UserSaveReq", description = "保存用户信息实体")
public class UserSaveReq {
    
    @Schema(description = "账号")
    @NotBlank(message = "账号不能为空")
    @Length(max = 30, message = "账号长度不能超过{max}")
    private String username;
    
    @Schema(description = "密码")
    @NotBlank(message = "密码不能为空")
    @Length(max = 64, message = "密码长度不能超过{max}")
    private String password;
    
    @Schema(description = "姓名")
    @NotBlank(message = "姓名不能为空")
    @Length(max = 50, message = "姓名长度不能超过50")
    private String nickName;
    
    @Schema(description = "手机")
    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误")
    private String mobile;
    
    @NotNull(message = "性别不能为空")
    private Sex sex;
}

// 来自 UserPageReq.java - 分页查询请求
@Data
@EqualsAndHashCode(callSuper = true)
public class UserPageReq extends PageRequest {
    
    @Schema(description = "用户名")
    private String username;
    
    @Schema(description = "昵称")
    private String nickName;
    
    @Schema(description = "状态")
    private Boolean status;
    
    @Schema(description = "组织ID")
    private Long orgId;
}

实体类定义

java
// 来自 User.java
@Data
@SuperBuilder
@NoArgsConstructor
@TableName("t_user")
@Schema(name = "User", description = "用户")
public class User extends SuperEntity<Long> {
    
    @Schema(description = "用户名")
    @DiffField(name = "用户名", strategy = DiffFieldStrategy.NOT_NULL)  // 差异对比字段
    private String username;
    
    @Schema(description = "租户ID")
    private Long tenantId;
    
    @Schema(description = "昵称")
    @DiffField(name = "昵称")
    private String nickName;
    
    @Schema(description = "手机号")
    @DiffField(name = "手机号")
    private String mobile;
    
    @Schema(description = "是否只读")
    private Boolean readonly;
    
    @Schema(description = "状态(false=禁用;true=启用)")
    private Boolean status;
    
    @Schema(description = "机构ID")
    private Long orgId;
}

异常处理

java
// 框架提供的 CheckedException
// 400 错误
throw CheckedException.badRequest("账号已存在");
throw CheckedException.badRequest("订单 {0} 不存在", orderId);

// 404 错误
throw CheckedException.notFound("用户不存在");

// 403 错误
throw CheckedException.forbidden("登录过期,请重新登录");

框架特有功能

常用注解速查

注解所属模块说明
@SaCheckPermissionSa-Token权限校验
@AccessLogcommon-framework操作日志记录
@DiffLogdiff-log-starter差异日志(自动记录修改前后对比)
@DiffFielddiff-log-starter标记需要对比的字段
@ResponseExceleasyexcel-starter一键导出 Excel
@TenantIgnoredb-starter忽略租户过滤
@DSTransactionaldb-starter分布式事务
@RemoteResultfeign-starter远程结果自动填充

常用工具类

类名说明
AuthenticationContext获取当前登录用户信息
CheckedException统一业务异常
BeanUtilPlusBean 转换增强
WrapsMyBatis-Plus 条件构造器简化
TenantHelper租户切换辅助
PasswordEncoderHelper密码加密

获取当前用户

java
@RequiredArgsConstructor
public class DemoService {
    
    private final AuthenticationContext context;
    
    public void demo() {
        Long userId = context.userId();       // 用户ID
        Long tenantId = context.tenantId();   // 租户ID
        String username = context.username(); // 用户名
        String nickname = context.nickname(); // 昵称
        List<String> roles = context.roles(); // 角色列表
    }
}

条件构造器简化

java
// 使用 Wraps 简化 LambdaQueryWrapper 创建
Wraps.<User>lbQ()
    .eq(User::getUsername, req.getUsername())
    .like(User::getNickName, req.getNickName())
    .eq(User::getStatus, req.getStatus())
    .in(User::getOrgId, orgIds);

租户切换

java
// 临时忽略租户过滤
TenantHelper.withIgnoreStrategy(() -> {
    return userMapper.selectById(userId);
});

// 切换到指定租户执行
TenantHelper.executeWithTenantDb("8888", () -> {
    return orderMapper.selectList(null);
});

下一步