Files
ruoyi-vue-pro/.claude/skills/modules/system/skill-system.yaml

840 lines
36 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Skill 文档 - yudao-module-system 模块
# 系统管理模块完整知识提取
skill:
id: "skill-system"
name: "System Module Skill"
version: "1.0.0"
module_path: "yudao-module-system"
created_at: "2026-03-18"
updated_at: "2026-03-18"
# ============================================
# 第一阶段:设计理念
# ============================================
philosophy:
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
business_position: |
yudao-module-system 是整个平台的核心基础设施模块,负责系统级的管理功能:
- 用户管理:管理后台用户的增删改查、密码管理、状态控制
- 角色管理基于RBAC模型的角色定义与权限分配
- 权限管理:菜单权限、按钮权限、数据权限的统一控制
- 部门管理:组织架构的树形结构管理
- 岗位管理:岗位信息的维护
- 租户管理:多租户架构的核心支撑,租户创建、套餐绑定、过期控制
- 字典管理:系统字典类型与字典数据的维护
- 通知公告:系统公告的发布与管理
- 短信/邮件/站内信:消息通知的三驾马车
- OAuth2开放授权协议支持
- 社交登录:第三方登录集成(微信、钉钉等)
该模块是其他所有业务模块的基础,提供统一的用户认证、权限校验、租户隔离能力。
# 设计原则遵循了哪些设计原则SOLID、DDD有哪些架构决策
design_principles:
- principle: "RBAC权限模型"
description: "采用基于角色的访问控制模型,用户-角色-菜单三层关联,支持细粒度的按钮级权限控制"
- principle: "多租户架构"
description: "通过TenantBaseDO基类实现租户数据隔离所有业务表自动携带tenant_id字段"
- principle: "数据权限"
description: "支持5种数据权限范围全部、自定义部门、本部门、本部门及子部门、仅本人"
- principle: "分层架构"
description: "严格遵循Controller-Service-DAL三层架构API层提供跨模块调用能力"
- principle: "接口隔离"
description: "Service接口与实现分离便于测试和扩展"
- principle: "缓存优先"
description: "权限数据使用Spring Cache + Redis缓存减少数据库查询压力"
- principle: "统一异常处理"
description: "通过ErrorCodeConstants定义统一错误码格式为1-002-XXX-XXX"
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
domain_model:
aggregates:
- name: "User用户聚合"
type: "聚合根"
entities: ["AdminUserDO", "UserRoleDO", "UserPostDO"]
description: "用户是系统的核心实体,关联角色和岗位,属于租户"
- name: "Role角色聚合"
type: "聚合根"
entities: ["RoleDO", "RoleMenuDO"]
description: "角色是权限的载体,关联菜单实现功能权限控制"
- name: "Menu菜单聚合"
type: "聚合根"
entities: ["MenuDO"]
description: "菜单是权限的最小单元,支持目录、菜单、按钮三种类型"
- name: "Dept部门聚合"
type: "聚合根"
entities: ["DeptDO"]
description: "部门是组织架构的核心,树形结构,支持数据权限"
- name: "Tenant租户聚合"
type: "聚合根"
entities: ["TenantDO", "TenantPackageDO"]
description: "租户是SaaS架构的核心绑定套餐控制功能范围"
value_objects:
- name: "Post岗位"
description: "岗位是用户的职位标识,不涉及复杂业务逻辑"
- name: "DictType/DictData字典"
description: "字典是系统配置的枚举值,全局共享不区分租户"
- name: "Notice通知公告"
description: "通知公告是系统级消息简单CRUD"
services:
- name: "PermissionService"
description: "权限领域服务,处理用户-角色-菜单的关联关系"
- name: "AdminAuthService"
description: "认证领域服务,处理登录、登出、令牌刷新"
- name: "TenantService"
description: "租户领域服务,处理租户生命周期管理"
# ============================================
# 第二阶段:架构设计
# ============================================
architecture:
# 分层架构
layers:
- name: "api"
purpose: "模块间API接口供其他模块RPC调用"
components:
- "PermissionApi: 权限校验接口"
- "AdminUserApi: 用户信息查询接口"
- "DeptApi: 部门信息查询接口"
- "PostApi: 岗位信息查询接口"
- "DictDataApi: 字典数据查询接口"
- "RoleApi: 角色信息查询接口"
- "SmsSendApi: 短信发送接口"
- "MailSendApi: 邮件发送接口"
- "NotifyMessageSendApi: 站内信发送接口"
- "SocialUserApi: 社交用户接口"
- "LoginLogApi: 登录日志接口"
- "OperateLogApi: 操作日志接口"
- name: "controller"
purpose: "HTTP接口供前端调用"
components:
- "AuthController: 认证相关(登录、登出、刷新令牌)"
- "UserController: 用户管理"
- "RoleController: 角色管理"
- "MenuController: 菜单管理"
- "DeptController: 部门管理"
- "PostController: 岗位管理"
- "DictTypeController/DictDataController: 字典管理"
- "TenantController: 租户管理"
- "TenantPackageController: 租户套餐管理"
- "SmsChannelController/SmsTemplateController: 短信管理"
- "MailAccountController/MailTemplateController: 邮件管理"
- "NotifyTemplateController/NotifyMessageController: 站内信管理"
- "OAuth2ClientController: OAuth2客户端管理"
- "SocialClientController: 社交客户端管理"
- name: "service"
purpose: "业务逻辑层"
components:
- "auth: AdminAuthService认证服务"
- "permission: PermissionService, RoleService, MenuService权限服务"
- "user: AdminUserService用户服务"
- "dept: DeptService, PostService组织服务"
- "dict: DictTypeService, DictDataService字典服务"
- "tenant: TenantService, TenantPackageService租户服务"
- "sms: SmsChannelService, SmsTemplateService, SmsSendService, SmsCodeService短信服务"
- "mail: MailAccountService, MailTemplateService, MailSendService邮件服务"
- "notify: NotifyTemplateService, NotifyMessageService, NotifySendService站内信服务"
- "oauth2: OAuth2ClientService, OAuth2TokenService, OAuth2CodeServiceOAuth2服务"
- "social: SocialClientService, SocialUserService社交服务"
- "logger: LoginLogService, OperateLogService日志服务"
- name: "dal"
purpose: "数据访问层"
components:
- "dataobject: 32个DO实体类"
- "mysql: MyBatis Mapper接口"
- "redis: Redis缓存操作"
# 设计模式应用
design_patterns:
- pattern: "策略模式"
location: "SmsChannelService, MailSendService"
purpose: "不同短信/邮件渠道的发送策略切换"
- pattern: "模板方法模式"
location: "NotifySendService, SmsSendService"
purpose: "消息发送的统一流程,子类实现具体发送逻辑"
- pattern: "工厂模式"
location: "SmsClientFactory, MailClientFactory"
purpose: "根据渠道类型创建对应的客户端实例"
- pattern: "代理模式"
location: "PermissionServiceImpl.getSelf()"
purpose: "解决Spring AOP缓存注解生效问题"
- pattern: "DTO模式"
location: "api/dto/*"
purpose: "跨模块数据传输对象,隔离内部实体"
- pattern: "转换器模式"
location: "convert/*"
purpose: "DO与VO之间的对象转换使用MapStruct"
# 模块间通信
communication:
apis: # 对外暴露的API
- api: "PermissionApi"
methods: ["hasAnyPermissions", "hasAnyRoles", "getUserRoleIdListByRoleIds"]
consumers: ["yudao-module-infra", "yudao-module-member"]
- api: "AdminUserApi"
methods: ["getUser", "getUserList"]
consumers: ["yudao-module-infra", "yudao-module-pay"]
- api: "DeptApi"
methods: ["getDept", "getDeptList"]
consumers: ["yudao-module-infra"]
- api: "DictDataApi"
methods: ["parseDictData", "getDictDataList"]
consumers: ["所有模块"]
- api: "SmsSendApi"
methods: ["sendSingleSms"]
consumers: ["yudao-module-member"]
- api: "MailSendApi"
methods: ["sendSingleMail"]
consumers: ["yudao-module-member"]
consumers: # 消费的其他模块API
- api: "PermissionCommonApi (framework-common)"
purpose: "基础权限校验能力"
- api: "FileApi (yudao-module-infra)"
purpose: "文件上传(用户头像等)"
mq: [] # 暂未使用消息队列
# ============================================
# 第三阶段:数据表设计
# ============================================
data_model:
# 实体继承体系
entity_hierarchy:
base: "BaseDO | TenantBaseDO"
description: |
- BaseDO: 包含id, creator, createTime, updater, updateTime, deleted字段
- TenantBaseDO: 继承BaseDO额外包含tenantId字段实现多租户隔离
- 使用@TenantIgnore注解标记不需要租户隔离的表如system_menu, system_dict_data
# 核心数据表
tables:
# ========== 用户相关 ==========
- name: "system_users"
comment: "用户表"
entity: "AdminUserDO"
extends: "TenantBaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "用户ID主键" }
- { name: "username", type: "VARCHAR(30)", comment: "用户账号" }
- { name: "password", type: "VARCHAR(100)", comment: "加密密码" }
- { name: "nickname", type: "VARCHAR(30)", comment: "用户昵称" }
- { name: "dept_id", type: "BIGINT", comment: "部门ID" }
- { name: "post_ids", type: "VARCHAR(255)", comment: "岗位编号数组JSON格式" }
- { name: "email", type: "VARCHAR(50)", comment: "用户邮箱" }
- { name: "mobile", type: "VARCHAR(20)", comment: "手机号码" }
- { name: "sex", type: "TINYINT", comment: "用户性别" }
- { name: "avatar", type: "VARCHAR(100)", comment: "用户头像" }
- { name: "status", type: "TINYINT", comment: "帐号状态0正常 1停用" }
- { name: "login_ip", type: "VARCHAR(50)", comment: "最后登录IP" }
- { name: "login_date", type: "DATETIME", comment: "最后登录时间" }
indexes:
- { name: "uk_username", columns: ["username", "tenant_id"] }
- { name: "uk_mobile", columns: ["mobile", "tenant_id"] }
- name: "system_user_role"
comment: "用户-角色关联表"
entity: "UserRoleDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "主键" }
- { name: "user_id", type: "BIGINT", comment: "用户ID" }
- { name: "role_id", type: "BIGINT", comment: "角色ID" }
indexes:
- { name: "idx_user_id", columns: ["user_id"] }
- { name: "idx_role_id", columns: ["role_id"] }
# ========== 角色相关 ==========
- name: "system_role"
comment: "角色表"
entity: "RoleDO"
extends: "TenantBaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "角色ID" }
- { name: "name", type: "VARCHAR(30)", comment: "角色名称" }
- { name: "code", type: "VARCHAR(100)", comment: "角色标识" }
- { name: "sort", type: "INT", comment: "角色排序" }
- { name: "status", type: "TINYINT", comment: "角色状态" }
- { name: "type", type: "TINYINT", comment: "角色类型1系统内置 2自定义" }
- { name: "data_scope", type: "TINYINT", comment: "数据范围" }
- { name: "data_scope_dept_ids", type: "VARCHAR(500)", comment: "数据范围部门ID数组" }
indexes:
- { name: "uk_code", columns: ["code", "tenant_id"] }
- name: "system_role_menu"
comment: "角色-菜单关联表"
entity: "RoleMenuDO"
extends: "TenantBaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "主键" }
- { name: "role_id", type: "BIGINT", comment: "角色ID" }
- { name: "menu_id", type: "BIGINT", comment: "菜单ID" }
indexes:
- { name: "idx_role_id", columns: ["role_id"] }
- { name: "idx_menu_id", columns: ["menu_id"] }
# ========== 菜单相关 ==========
- name: "system_menu"
comment: "菜单表"
entity: "MenuDO"
extends: "BaseDO"
annotation: "@TenantIgnore"
columns:
- { name: "id", type: "BIGINT", comment: "菜单ID" }
- { name: "name", type: "VARCHAR(50)", comment: "菜单名称" }
- { name: "permission", type: "VARCHAR(100)", comment: "权限标识" }
- { name: "type", type: "TINYINT", comment: "菜单类型1目录 2菜单 3按钮" }
- { name: "sort", type: "INT", comment: "显示顺序" }
- { name: "parent_id", type: "BIGINT", comment: "父菜单ID" }
- { name: "path", type: "VARCHAR(200)", comment: "路由地址" }
- { name: "icon", type: "VARCHAR(100)", comment: "菜单图标" }
- { name: "component", type: "VARCHAR(255)", comment: "组件路径" }
- { name: "component_name", type: "VARCHAR(100)", comment: "组件名" }
- { name: "status", type: "TINYINT", comment: "菜单状态" }
- { name: "visible", type: "BIT", comment: "是否可见" }
- { name: "keep_alive", type: "BIT", comment: "是否缓存" }
- { name: "always_show", type: "BIT", comment: "是否总是显示" }
# ========== 部门相关 ==========
- name: "system_dept"
comment: "部门表"
entity: "DeptDO"
extends: "TenantBaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "部门ID" }
- { name: "name", type: "VARCHAR(30)", comment: "部门名称" }
- { name: "parent_id", type: "BIGINT", comment: "父部门ID" }
- { name: "sort", type: "INT", comment: "显示顺序" }
- { name: "leader_user_id", type: "BIGINT", comment: "负责人" }
- { name: "phone", type: "VARCHAR(20)", comment: "联系电话" }
- { name: "email", type: "VARCHAR(50)", comment: "邮箱" }
- { name: "status", type: "TINYINT", comment: "部门状态" }
- name: "system_post"
comment: "岗位表"
entity: "PostDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "岗位ID" }
- { name: "name", type: "VARCHAR(50)", comment: "岗位名称" }
- { name: "code", type: "VARCHAR(64)", comment: "岗位编码" }
- { name: "sort", type: "INT", comment: "显示顺序" }
- { name: "status", type: "TINYINT", comment: "状态" }
# ========== 租户相关 ==========
- name: "system_tenant"
comment: "租户表"
entity: "TenantDO"
extends: "BaseDO"
annotation: "@TenantIgnore"
columns:
- { name: "id", type: "BIGINT", comment: "租户编号" }
- { name: "name", type: "VARCHAR(100)", comment: "租户名" }
- { name: "contact_user_id", type: "BIGINT", comment: "联系人的用户编号" }
- { name: "contact_name", type: "VARCHAR(100)", comment: "联系人" }
- { name: "contact_mobile", type: "VARCHAR(20)", comment: "联系手机" }
- { name: "status", type: "TINYINT", comment: "租户状态" }
- { name: "websites", type: "VARCHAR(500)", comment: "绑定域名列表" }
- { name: "package_id", type: "BIGINT", comment: "租户套餐编号" }
- { name: "expire_time", type: "DATETIME", comment: "过期时间" }
- { name: "account_count", type: "INT", comment: "账号数量" }
- name: "system_tenant_package"
comment: "租户套餐表"
entity: "TenantPackageDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "套餐编号" }
- { name: "name", type: "VARCHAR(100)", comment: "套餐名" }
- { name: "status", type: "TINYINT", comment: "套餐状态" }
- { name: "menu_ids", type: "VARCHAR(2000)", comment: "关联菜单编号数组" }
- { name: "remark", type: "VARCHAR(255)", comment: "备注" }
# ========== 字典相关 ==========
- name: "system_dict_type"
comment: "字典类型表"
entity: "DictTypeDO"
extends: "BaseDO"
annotation: "@TenantIgnore"
columns:
- { name: "id", type: "BIGINT", comment: "字典主键" }
- { name: "name", type: "VARCHAR(100)", comment: "字典名称" }
- { name: "type", type: "VARCHAR(100)", comment: "字典类型" }
- { name: "status", type: "TINYINT", comment: "字典状态" }
- name: "system_dict_data"
comment: "字典数据表"
entity: "DictDataDO"
extends: "BaseDO"
annotation: "@TenantIgnore"
columns:
- { name: "id", type: "BIGINT", comment: "字典主键" }
- { name: "sort", type: "INT", comment: "字典排序" }
- { name: "label", type: "VARCHAR(100)", comment: "字典标签" }
- { name: "value", type: "VARCHAR(100)", comment: "字典值" }
- { name: "dict_type", type: "VARCHAR(100)", comment: "字典类型" }
- { name: "status", type: "TINYINT", comment: "状态" }
- { name: "color_type", type: "VARCHAR(20)", comment: "颜色类型" }
# ========== 短信相关 ==========
- name: "system_sms_channel"
comment: "短信渠道表"
entity: "SmsChannelDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "渠道编号" }
- { name: "signature", type: "VARCHAR(12)", comment: "短信签名" }
- { name: "code", type: "VARCHAR(63)", comment: "渠道编码" }
- { name: "status", type: "TINYINT", comment: "开启状态" }
- { name: "api_key", type: "VARCHAR(128)", comment: "短信API密钥" }
- { name: "callback_url", type: "VARCHAR(255)", comment: "短信回调URL" }
- name: "system_sms_template"
comment: "短信模板表"
entity: "SmsTemplateDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "模板编号" }
- { name: "name", type: "VARCHAR(63)", comment: "模板名称" }
- { name: "code", type: "VARCHAR(63)", comment: "模板编码" }
- { name: "content", type: "VARCHAR(255)", comment: "模板内容" }
- { name: "channel_id", type: "BIGINT", comment: "短信渠道编号" }
- { name: "channel_code", type: "VARCHAR(63)", comment: "短信渠道编码" }
- { name: "status", type: "TINYINT", comment: "开启状态" }
- name: "system_sms_log"
comment: "短信日志表"
entity: "SmsLogDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "日志编号" }
- { name: "channel_id", type: "BIGINT", comment: "短信渠道编号" }
- { name: "template_id", type: "BIGINT", comment: "模板编号" }
- { name: "template_code", type: "VARCHAR(63)", comment: "模板编码" }
- { name: "template_content", type: "VARCHAR(255)", comment: "模板内容" }
- { name: "template_params", type: "VARCHAR(255)", comment: "模板参数" }
- { name: "mobile", type: "VARCHAR(11)", comment: "手机号" }
- { name: "send_status", type: "TINYINT", comment: "发送状态" }
- { name: "send_time", type: "DATETIME", comment: "发送时间" }
- { name: "receive_status", type: "TINYINT", comment: "接收状态" }
- { name: "receive_time", type: "DATETIME", comment: "接收时间" }
# ========== 邮件相关 ==========
- name: "system_mail_account"
comment: "邮箱账号表"
entity: "MailAccountDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "主键" }
- { name: "mail", type: "VARCHAR(100)", comment: "邮箱" }
- { name: "username", type: "VARCHAR(100)", comment: "用户名" }
- { name: "password", type: "VARCHAR(100)", comment: "密码" }
- { name: "host", type: "VARCHAR(100)", comment: "SMTP服务器域名" }
- { name: "port", type: "INT", comment: "SMTP服务器端口" }
- { name: "ssl_enable", type: "BIT", comment: "是否开启SSL" }
- name: "system_mail_template"
comment: "邮件模板表"
entity: "MailTemplateDO"
extends: "BaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "主键" }
- { name: "name", type: "VARCHAR(63)", comment: "模板名称" }
- { name: "code", type: "VARCHAR(63)", comment: "模板编码" }
- { name: "account_id", type: "BIGINT", comment: "发送的邮箱账号编号" }
- { name: "nickname", type: "VARCHAR(255)", comment: "发送人名称" }
- { name: "title", type: "VARCHAR(255)", comment: "邮件标题" }
- { name: "content", type: "VARCHAR(10240)", comment: "邮件内容" }
- { name: "status", type: "TINYINT", comment: "开启状态" }
# ========== OAuth2相关 ==========
- name: "system_oauth2_client"
comment: "OAuth2客户端表"
entity: "OAuth2ClientDO"
extends: "TenantBaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "编号" }
- { name: "client_id", type: "VARCHAR(255)", comment: "客户端编号" }
- { name: "secret", type: "VARCHAR(255)", comment: "客户端密钥" }
- { name: "name", type: "VARCHAR(255)", comment: "应用名" }
- { name: "logo", type: "VARCHAR(255)", comment: "应用图标" }
- { name: "authorized_grant_types", type: "VARCHAR(255)", comment: "授权类型" }
- { name: "scopes", type: "VARCHAR(255)", comment: "授权范围" }
- { name: "redirect_uris", type: "VARCHAR(255)", comment: "回调地址" }
- { name: "access_token_validity_seconds", type: "INT", comment: "访问令牌有效期" }
- { name: "refresh_token_validity_seconds", type: "INT", comment: "刷新令牌有效期" }
- name: "system_oauth2_access_token"
comment: "OAuth2访问令牌表"
entity: "OAuth2AccessTokenDO"
extends: "TenantBaseDO"
columns:
- { name: "id", type: "BIGINT", comment: "编号" }
- { name: "access_token", type: "VARCHAR(255)", comment: "访问令牌" }
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
- { name: "user_type", type: "TINYINT", comment: "用户类型" }
- { name: "client_id", type: "VARCHAR(255)", comment: "客户端编号" }
- { name: "expires_time", type: "DATETIME", comment: "过期时间" }
# 表关系ER关系
relationships:
- from: "system_users"
to: "system_dept"
type: "N:1"
foreign_key: "dept_id"
description: "用户属于一个部门"
- from: "system_users"
to: "system_post"
type: "N:N"
foreign_key: "post_ids (JSON数组)"
description: "用户可关联多个岗位"
- from: "system_user_role"
to: "system_users"
type: "N:1"
foreign_key: "user_id"
- from: "system_user_role"
to: "system_role"
type: "N:1"
foreign_key: "role_id"
- from: "system_role_menu"
to: "system_role"
type: "N:1"
foreign_key: "role_id"
- from: "system_role_menu"
to: "system_menu"
type: "N:1"
foreign_key: "menu_id"
- from: "system_menu"
to: "system_menu"
type: "N:1"
foreign_key: "parent_id"
description: "菜单树形结构"
- from: "system_dept"
to: "system_dept"
type: "N:1"
foreign_key: "parent_id"
description: "部门树形结构"
- from: "system_tenant"
to: "system_tenant_package"
type: "N:1"
foreign_key: "package_id"
- from: "system_dict_data"
to: "system_dict_type"
type: "N:1"
foreign_key: "dict_type"
- from: "system_sms_template"
to: "system_sms_channel"
type: "N:1"
foreign_key: "channel_id"
- from: "system_mail_template"
to: "system_mail_account"
type: "N:1"
foreign_key: "account_id"
# ============================================
# 第四阶段:代码使用设计
# ============================================
code_patterns:
# Controller层规范
controller:
annotations:
- "@Tag(name = '管理后台 - XXX'): OpenAPI 3.0 文档标签"
- "@RestController: RESTful控制器"
- "@RequestMapping('/system/xxx'): 路由前缀"
- "@Validated: 参数校验"
- "@Operation(summary = 'XXX'): 接口文档说明"
- "@PreAuthorize('@ss.hasPermission('system:xxx:add')'): 权限校验"
- "@PermitAll: 允许匿名访问(如登录接口)"
example: |
@Tag(name = "管理后台 - 用户")
@RestController
@RequestMapping("/system/user")
@Validated
public class UserController {
@PostMapping("/create")
@Operation(summary = "创建用户")
@PreAuthorize("@ss.hasPermission('system:user:create')")
public CommonResult<Long> createUser(@Valid @RequestBody UserSaveReqVO createReqVO) {
return success(userService.createUser(createReqVO));
}
}
# Service层规范
service:
interface_pattern: |
- 接口定义在 service 包下
- 方法参数使用 @Valid 注解进行校验
- 返回值使用具体类型或 void
impl_pattern: |
- 实现类放在 service 包下,命名为 XxxServiceImpl
- 使用 @Service 注解
- 使用 @Resource 注入 Mapper 和其他 Service
- 使用 @Transactional(rollbackFor = Exception.class) 标记事务方法
- 使用 @Cacheable/@CacheEvict 进行缓存操作
example: |
@Service
@Slf4j
public class PermissionServiceImpl implements PermissionService {
@Resource
private RoleMenuMapper roleMenuMapper;
@Resource
private UserRoleMapper userRoleMapper;
@Override
@DSTransactional // 多数据源事务
@Caching(evict = {
@CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, allEntries = true),
@CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, allEntries = true)
})
public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
// 业务逻辑
}
}
# 数据访问规范
dal:
mapper_pattern: |
- Mapper 接口继承 BaseMapperX<DO类>
- 使用 @Mapper 注解
- 复杂查询使用 MPJLambdaWrapperWrapper 或 XML
example: |
@Mapper
public interface UserMapper extends BaseMapperX<AdminUserDO> {
default AdminUserDO selectByUsername(String username) {
return selectOne(AdminUserDO::getUsername, username);
}
default List<AdminUserDO> selectListByDeptIds(Collection<Long> deptIds) {
return selectList(new LambdaQueryWrapperX<AdminUserDO>()
.inIfPresent(AdminUserDO::getDeptId, deptIds));
}
}
# 异常处理
error_handling:
code_prefix: "1-002-XXX-XXX"
format: "模块号-子模块号-序号"
examples:
- code: "1_002_000_000"
message: "登录失败,账号密码不正确"
module: "AUTH"
- code: "1_002_001_000"
message: "已经存在该名字的菜单"
module: "MENU"
- code: "1_002_002_000"
message: "角色不存在"
module: "ROLE"
- code: "1_002_003_000"
message: "用户账号已经存在"
module: "USER"
- code: "1_002_004_000"
message: "已经存在该名字的部门"
module: "DEPT"
- code: "1_002_015_000"
message: "租户不存在"
module: "TENANT"
usage: |
// 在Service中抛出业务异常
if (user == null) {
throw exception(USER_NOT_EXISTS);
}
// 带参数的异常
throw exception(ROLE_NAME_DUPLICATE, name);
# 统一响应
response:
success_pattern: "CommonResult.success(data)"
error_pattern: "CommonResult.error(errorCode)"
usage: |
// 成功响应
return success(userService.getUser(id));
// 失败响应(通常由全局异常处理器处理)
throw exception(USER_NOT_EXISTS);
# ============================================
# 第五阶段:扩展指南
# ============================================
extension_guide:
# 新增功能步骤
new_feature:
title: "新增业务功能(以新增'操作日志'功能为例)"
steps:
- step: "1. 创建DO实体类"
detail: |
在 dal/dataobject/logger/ 下创建 OperateLogDO.java
继承 BaseDO 或 TenantBaseDO
使用 @TableName 指定表名
- step: "2. 创建Mapper接口"
detail: |
在 dal/mysql/logger/ 下创建 OperateLogMapper.java
继承 BaseMapperX<OperateLogDO>
- step: "3. 创建Service接口和实现"
detail: |
在 service/logger/ 下创建 OperateLogService.java 接口
在 service/logger/ 下创建 OperateLogServiceImpl.java 实现
使用 @Service 和 @Resource 注解
- step: "4. 创建Controller"
detail: |
在 controller/admin/logger/ 下创建 OperateLogController.java
使用 @RestController, @RequestMapping, @Tag 等注解
使用 @PreAuthorize 进行权限控制
- step: "5. 创建VO类"
detail: |
在 controller/admin/logger/vo/ 下创建请求和响应VO
使用 @Data, @Valid 等注解进行参数校验
- step: "6. 添加错误码"
detail: |
在 enums/ErrorCodeConstants.java 中添加错误码
格式ErrorCode XXX_NOT_EXISTS = new ErrorCode(1_002_XXX_000, "XXX不存在");
- step: "7. 添加菜单权限"
detail: |
在数据库 system_menu 表中添加菜单记录
配置权限标识如 system:operate-log:query
# 新增渠道/类型示例
new_channel:
title: "新增短信渠道(以新增'华为云短信'为例)"
steps:
- step: "1. 添加渠道枚举"
detail: |
在 SmsChannelEnum 中添加新渠道类型
HUAWEI("huawei", "华为云")
- step: "2. 创建客户端实现"
detail: |
在 framework/sms/core/client/impl/ 下创建 HuaweiSmsClient.java
继承 AbstractSmsClient 抽象类
实现 doSendSms() 方法
- step: "3. 注册到工厂"
detail: |
在 SmsClientFactory 中注册新渠道
clients.put(SmsChannelEnum.HUAWEI, new HuaweiSmsClient());
- step: "4. 添加配置类"
detail: |
创建 HuaweiSmsChannelProperties 配置类
继承 SmsChannelProperties 基类
# 新增字典类型
new_dict:
title: "新增字典类型"
steps:
- step: "1. 通过管理后台添加"
detail: |
登录管理后台 -> 系统管理 -> 字典管理 -> 新增字典类型
填写字典名称、字典类型(如 system_user_sex
- step: "2. 添加字典数据"
detail: |
在字典类型下添加字典数据项
设置字典标签、字典值、排序、状态等
- step: "3. 代码中使用"
detail: |
// 获取字典数据列表
List<DictDataRespDTO> dictList = dictDataApi.getDictDataList("system_user_sex");
// 解析字典值
String label = dictDataApi.parseDictData("system_user_sex", "1");
# 最佳实践
best_practices:
- practice: "权限标识命名规范"
detail: "使用 '模块:功能:操作' 格式,如 system:user:create、system:role:update"
- practice: "数据权限使用"
detail: "在Service方法上使用 @DataPermission 注解控制数据权限,使用 enable=false 关闭"
- practice: "缓存使用"
detail: "权限相关数据使用 @Cacheable 缓存,修改时使用 @CacheEvict 清除"
- practice: "多租户注意"
detail: "不需要租户隔离的表使用 @TenantIgnore 注解,如字典表、菜单表"
- practice: "事务处理"
detail: "跨数据源操作使用 @DSTransactional单数据源使用 @Transactional"
- practice: "日志记录"
detail: "使用 @OperateLog 注解记录操作日志,使用 Lombok 的 @Slf4j 记录调试日志"
- practice: "参数校验"
detail: "VO类使用 @Valid、@NotNull、@NotBlank 等注解进行参数校验"
- practice: "API设计"
detail: "跨模块调用通过 API 层,不要直接调用 Service 层"
# ============================================
# 依赖关系
# ============================================
dependencies:
# 内部依赖(其他模块)
internal:
- module: "yudao-framework-common"
api: "CommonResult, PageResult, ErrorCode"
purpose: "通用响应、分页、错误码"
- module: "yudao-framework-security"
api: "SecurityFrameworkUtils, @PreAuthorize"
purpose: "安全框架、权限注解"
- module: "yudao-framework-tenant"
api: "TenantBaseDO, @TenantIgnore"
purpose: "多租户支持"
- module: "yudao-framework-mybatis"
api: "BaseMapperX, BaseDO"
purpose: "MyBatis增强"
- module: "yudao-framework-redis"
api: "@Cacheable, RedisKeyConstants"
purpose: "Redis缓存"
- module: "yudao-module-infra"
api: "FileApi"
purpose: "文件上传"
# 外部依赖(第三方库)
external:
- name: "spring-boot-starter-web"
version: "2.7.x"
purpose: "Web框架"
- name: "mybatis-plus-boot-starter"
version: "3.5.x"
purpose: "MyBatis增强"
- name: "spring-boot-starter-security"
version: "2.7.x"
purpose: "安全框架"
- name: "spring-boot-starter-validation"
version: "2.7.x"
purpose: "参数校验"
- name: "spring-boot-starter-cache"
version: "2.7.x"
purpose: "缓存支持"
- name: "mapstruct"
version: "1.5.x"
purpose: "对象转换"
- name: "lombok"
version: "1.18.x"
purpose: "代码简化"
- name: "hutool-all"
version: "5.8.x"
purpose: "工具类库"
- name: "knife4j-spring-boot-starter"
version: "3.0.x"
purpose: "API文档"
# ============================================
# 关键文件清单
# ============================================
key_files:
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java"
purpose: "错误码常量定义,所有业务异常的统一入口"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java"
purpose: "用户实体类,核心聚合根"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java"
purpose: "角色实体类,权限载体"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java"
purpose: "菜单实体类,权限最小单元"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java"
purpose: "权限服务实现,核心业务逻辑"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java"
purpose: "认证服务实现,登录登出逻辑"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java"
purpose: "认证控制器,登录接口入口"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java"
purpose: "权限API接口跨模块调用入口"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java"
purpose: "数据权限枚举5种数据范围定义"
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java"
purpose: "菜单类型枚举,目录/菜单/按钮"