ruoyi-vue-plus-code-patterns
- Repo stars 7
- Author updated Live
- Author repo claude-skill-registry-data
- Domain
- Data
- Compatible agents
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- Trust score
- 88 / 100 · community maintained
- Author / version / license
- @majiayu000 · no license declared
- Token usage
- Moderate
- Setup complexity
- Guided setup
- External API key
- Not required
- Operating systems
- Unspecified (assume cross-platform)
- Runtime requirements
- No special requirements
- Permissions
-
- Read-only
- Write / modify
- Network behavior
- External requests
- Install commands
- 26 variants
Profile is derived at build time from SKILL.md and install vectors. Subject to drift from author intent.
Heads up: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: ruoyi-vue-plus-code-patterns
description: | 前端请求 → Bo/DTO(Controller接收) → Entity(Service处理) → Vo(Controller返回) → 前端展示 package com.ruoyi.we…
category: data
runtime: no special runtime
---
# ruoyi-vue-plus-code-patterns output preview
## PART A: Task fit
- Use case: | 前端请求 → Bo/DTO(Controller接收) → Entity(Service处理) → Vo(Controller返回) → 前端展示 package com.ruoyi.web.controller.system; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; makes outbound network calls. Works with Claude Code, Cursor, Cline and 23 more..
- Inputs: target material, constraints, expected output, and acceptance criteria.
- Evidence boundary: follow “核心规范 / 规范1:分层架构与对象映射规范 / 规范2:Vue3 组合式 API (Composition API) 编码标准” and do not present inference as author intent.
## PART B: Execution result
- **01** The card summarizes the use case; runtime output centers on “| 前端请求 → Bo/DTO(Controller接收) → Entity(Service处理) → Vo(Controller返回) → 前端展示 package com.ruoyi.web.controller.system; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; makes outbound network calls. Works with Claude Code, Cursor, Cline and 23 more.”.
- **02** When the source has headings, the agent prioritizes “核心规范 / 规范1:分层架构与对象映射规范 / 规范2:Vue3 组合式 API (Composition API) 编码标准” so the result follows the author’s structure.
- **03** Typical output includes task judgment, concrete steps, required commands or file edits, validation, and follow-up options.
- **04** Risk context follows the fingerprint: read files, write/modify files; may access external network resources; usually needs no extra API key.
## Running Rules
- read files, write/modify files; may access external network resources; usually needs no extra API key.
- Validate with a small sample before expanding scope.
- Return the result, validation criteria, and next iteration options. The source does not require a stable slash command. After installation, invoke the skill by name and describe the task.
Name target files or source material, expected output, forbidden changes, and whether network or shell access is allowed. Permission fingerprint: read files, write/modify files.
Start with a small task and check whether the result follows “核心规范 / 规范1:分层架构与对象映射规范 / 规范2:Vue3 组合式 API (Composition API) 编码标准”. Inspect diffs, logs, previews, or tests before expanding scope.
Confirm the final output includes a concrete result, evidence, and next action. If it stays generic, tighten inputs, boundaries, and acceptance criteria.
---
name: ruoyi-vue-plus-code-patterns
description: | 前端请求 → Bo/DTO(Controller接收) → Entity(Service处理) → Vo(Controller返回) → 前端展示 package com.ruoyi.we…
category: data
source: majiayu000/claude-skill-registry-data
---
# ruoyi-vue-plus-code-patterns
## When to use
- | 前端请求 → Bo/DTO(Controller接收) → Entity(Service处理) → Vo(Controller返回) → 前端展示 package com.ruoyi.web.controller.system; i…
- Use it when the task has clear inputs, repeatable steps, and validation criteria.
## What to provide
- Target material, scope, expected result, and forbidden changes.
- Whether network, commands, file writes, or external services are allowed.
## Execution rules
- Organize steps around “核心规范 / 规范1:分层架构与对象映射规范 / 规范2:Vue3 组合式 API (Composition API) 编码标准” and keep inference separate from source facts.
- read files, write/modify files; may access external network resources; usually needs no extra API key.
- Validate with a small sample before expanding the task.
## Output requirements
- Return the deliverable, key evidence, validation method, and next action.
- Mark missing information as unknown; do not invent commands, platforms, or dependencies. The author source anchors workflow facts; repository files anchor sources and commands; Fluxly only adds fit, limitations, and quality judgment.
skill "ruoyi-vue-plus-code-patterns" {
input -> user goal + target files + boundaries + acceptance criteria
context -> 核心规范 / 规范1:分层架构与对象映射规范 / 规范2:Vue3 组合式 API (Composition API) 编码标准
rules -> SKILL.md triggers / order / output contract
runtime -> no special runtime | read files, write/modify files | may access external network resources
guardrails -> usually needs no extra API key + small-sample validation + diff/log review
output -> copyable result + checklist + next iteration
} 若依框架代码规范指南
核心规范
规范1:分层架构与对象映射规范
架构层次划分:
- Controller层(控制器层):负责接收HTTP请求、参数校验、权限控制、响应封装
- Service层(业务逻辑层):负责业务逻辑处理、事务控制、数据组装
- Mapper层(数据访问层):负责数据库CRUD操作,仅处理数据持久化
对象流转规范:
Bo (Business Object) / DTO (Data Transfer Object):前端传入的数据传输对象
- 用于接收前端表单数据
- 包含校验注解(@NotNull、@NotBlank等)
- 命名规则:
实体名称Bo.java(如 SysOrderBo.java)
Entity (实体对象):数据库表映射对象
- 与数据库表结构一一对应
- 仅在Service层和Mapper层使用
- 包含MyBatis-Plus注解(@TableName、@TableId等)
- 命名规则:
实体名称.java(如 SysOrder.java)
Vo (View Object):返回给前端的视图对象
- 用于返回给前端展示的数据
- 可能包含关联对象、计算字段、格式化数据
- 严禁直接返回Entity,避免暴露敏感字段(密码、盐值等)
- 命名规则:
实体名称Vo.java(如 SysOrderVo.java)
对象转换工具:
- 优先使用:
BeanUtil.copyProperties(source, targetClass)(若依封装的Hutool工具) - 复杂场景使用:MapStruct(编译期生成转换代码,性能更优)
- 严禁手写大量setter/getter进行属性拷贝
完整数据流转示例:
前端请求 → Bo/DTO(Controller接收) → Entity(Service处理) → Vo(Controller返回) → 前端展示
Controller层完整示例:
package com.ruoyi.web.controller.system;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.bean.BeanUtil;
import com.ruoyi.system.domain.SysOrder;
import com.ruoyi.system.domain.bo.SysOrderBo;
import com.ruoyi.system.domain.vo.SysOrderVo;
import com.ruoyi.system.service.ISysOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 订单管理Controller
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/order")
public class SysOrderController extends BaseController {
@Autowired
private ISysOrderService orderService;
/**
* 查询订单列表
* 返回类型:TableDataInfo(包含分页信息)
*/
@PreAuthorize("@ss.hasPermi('system:order:list')")
@GetMapping("/list")
public TableDataInfo list(SysOrderBo bo) {
// 开启分页(从BaseController继承)
startPage();
// 查询数据(Service返回Vo列表)
List<SysOrderVo> list = orderService.selectOrderList(bo);
// 封装分页响应
return getDataTable(list);
}
/**
* 获取订单详细信息
* 返回类型:AjaxResult(包含单个Vo对象)
*/
@PreAuthorize("@ss.hasPermi('system:order:query')")
@GetMapping(value = "/{orderId}")
public AjaxResult getInfo(@PathVariable Long orderId) {
return success(orderService.selectOrderById(orderId));
}
/**
* 新增订单
* 接收流程:Bo (DTO) → 转换 Entity → Service处理 → 返回操作结果
*/
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysOrderBo bo) {
// 1. Bo (DTO) 转 Entity
SysOrder order = BeanUtil.copyProperties(bo, SysOrder.class);
// 2. 调用Service(Service负责业务逻辑处理)
boolean result = orderService.insertOrder(order);
// 3. 返回统一响应(toAjax方法会根据boolean返回成功/失败信息)
return toAjax(result);
}
/**
* 修改订单
* 必须先校验数据权限(是否有权限修改该订单)
*/
@PreAuthorize("@ss.hasPermi('system:order:edit')")
@Log(title = "订单管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysOrderBo bo) {
SysOrder order = BeanUtil.copyProperties(bo, SysOrder.class);
return toAjax(orderService.updateOrder(order));
}
/**
* 删除订单(支持批量删除)
* 参数:订单ID数组
*/
@PreAuthorize("@ss.hasPermi('system:order:remove')")
@Log(title = "订单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{orderIds}")
public AjaxResult remove(@PathVariable Long[] orderIds) {
return toAjax(orderService.deleteOrderByIds(orderIds));
}
/**
* 导出订单数据(Excel)
*/
@PreAuthorize("@ss.hasPermi('system:order:export')")
@Log(title = "订单管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysOrderBo bo) {
List<SysOrderVo> list = orderService.selectOrderList(bo);
ExcelUtil<SysOrderVo> util = new ExcelUtil<>(SysOrderVo.class);
util.exportExcel(response, list, "订单数据");
}
}
Service层实现示例:
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtil;
import com.ruoyi.system.domain.SysOrder;
import com.ruoyi.system.domain.bo.SysOrderBo;
import com.ruoyi.system.domain.vo.SysOrderVo;
import com.ruoyi.system.mapper.SysOrderMapper;
import com.ruoyi.system.service.ISysOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* 订单服务实现
*/
@Service
public class SysOrderServiceImpl implements ISysOrderService {
@Autowired
private SysOrderMapper orderMapper;
/**
* 查询订单列表
* Entity → Vo 转换
*/
@Override
public List<SysOrderVo> selectOrderList(SysOrderBo bo) {
// 1. 构建查询条件(使用MyBatis-Plus的LambdaQueryWrapper)
LambdaQueryWrapper<SysOrder> wrapper = Wrappers.lambdaQuery();
wrapper.like(StringUtils.isNotBlank(bo.getOrderNo()),
SysOrder::getOrderNo, bo.getOrderNo())
.eq(bo.getStatus() != null,
SysOrder::getStatus, bo.getStatus())
.between(bo.getBeginTime() != null && bo.getEndTime() != null,
SysOrder::getCreateTime, bo.getBeginTime(), bo.getEndTime());
// 2. 查询数据库
List<SysOrder> list = orderMapper.selectList(wrapper);
// 3. Entity → Vo 转换(使用Stream流处理)
return list.stream()
.map(entity -> BeanUtil.copyProperties(entity, SysOrderVo.class))
.collect(Collectors.toList());
}
/**
* 新增订单(带事务)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean insertOrder(SysOrder order) {
// 业务逻辑处理
order.setOrderNo(generateOrderNo()); // 生成订单号
order.setCreateBy(SecurityUtils.getUsername()); // 设置创建人
// 插入数据库(MyBatis-Plus方法)
return orderMapper.insert(order) > 0;
}
/**
* 批量删除(带事务)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteOrderByIds(Long[] orderIds) {
// 使用MyBatis-Plus的批量删除
return orderMapper.deleteBatchIds(Arrays.asList(orderIds)) > 0;
}
/**
* 生成订单号(业务逻辑)
*/
private String generateOrderNo() {
return "ORD" + System.currentTimeMillis();
}
}
规范2:Vue3 组合式 API (Composition API) 编码标准
核心原则:
- 所有新组件必须使用
<script setup lang="ts">语法糖 - 禁止使用 Options API(
export default {})编写新组件 - 优先使用 TypeScript 提供类型安全
响应式数据定义规范:
ref() - 用于基本类型和单一对象引用
- 适用场景:字符串、数字、布尔值、单个对象引用
- 访问方式:通过
.value访问/修改 - 模板中自动解包,无需
.value
reactive() - 用于复杂对象和表单数据
- 适用场景:表单对象、查询参数、状态管理
- 特性:深度响应式,自动解包嵌套的ref
- 注意:解构会丢失响应性,需使用
toRefs()转换
toRefs() - 保持解构后的响应性
- 使用场景:需要解构 reactive 对象时
- 作用:将 reactive 对象的每个属性转为 ref
组件通信规范:
父传子:defineProps(类型定义)
// 使用 TypeScript 类型定义(推荐) interface Props { orderId: number; orderData?: SysOrder; // 可选属性 } const props = defineProps<Props>(); // 或使用 withDefaults 定义默认值 const props = withDefaults(defineProps<Props>(), { orderData: () => ({}) });子传父:defineEmits(事件定义)
// 定义事件类型 interface Emits { (e: 'update:modelValue', value: string): void; (e: 'submit', data: FormData): void; } const emit = defineEmits<Emits>(); // 触发事件 emit('submit', formData);跨层级通信:provide/inject(谨慎使用)
- 仅用于深层组件树共享状态
- 优先使用 props/emits 保持数据流清晰
生命周期钩子:
onMounted()- 组件挂载后(替代 mounted)onBeforeUnmount()- 组件卸载前(替代 beforeDestroy)onUpdated()- 组件更新后watch()/watchEffect()- 侦听响应式数据变化
完整示例:
<template>
<div class="app-container">
<!-- 搜索栏 -->
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch">
<el-form-item label="订单编号" prop="orderNo">
<el-input
v-model="queryParams.orderNo"
placeholder="请输入订单编号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="订单状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择订单状态" clearable>
<el-option
v-for="dict in sys_order_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['system:order:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:order:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:order:remove']"
>删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="orderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="订单编号" align="center" prop="orderNo" />
<el-table-column label="订单金额" align="center" prop="amount" />
<el-table-column label="订单状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_order_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:order:edit']"
>修改</el-button>
<el-button
type="text"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:order:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改订单对话框 -->
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="订单编号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入订单编号" />
</el-form-item>
<el-form-item label="订单金额" prop="amount">
<el-input-number v-model="form.amount" :precision="2" :min="0" />
</el-form-item>
<el-form-item label="订单状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_order_status"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="OrderList">
import { ref, reactive, toRefs, computed, onMounted, nextTick } from 'vue';
import { listOrder, getOrder, addOrder, updateOrder, delOrder } from '@/api/system/order';
import { SysOrderVo, SysOrderForm, SysOrderQuery } from '@/api/system/order/types';
import type { FormInstance, FormRules } from 'element-plus';
// ==================== 1. 响应式数据定义 ====================
// 基本类型使用 ref
const loading = ref(false);
const showSearch = ref(true);
const open = ref(false);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref('');
// 列表数据使用 ref
const orderList = ref<SysOrderVo[]>([]);
const ids = ref<Array<string | number>>([]);
// 表单对象使用 reactive(配合双向绑定)
const queryParams = reactive<SysOrderQuery>({
pageNum: 1,
pageSize: 10,
orderNo: '',
status: undefined,
beginTime: undefined,
endTime: undefined
});
// 表单数据使用 reactive
const form = reactive<SysOrderForm>({
orderId: undefined,
orderNo: '',
amount: 0,
status: '0',
remark: ''
});
// 表单校验规则
const rules = reactive<FormRules>({
orderNo: [
{ required: true, message: '订单编号不能为空', trigger: 'blur' }
],
amount: [
{ required: true, message: '订单金额不能为空', trigger: 'blur' }
],
status: [
{ required: true, message: '订单状态不能为空', trigger: 'change' }
]
});
// ==================== 2. 引用和字典 ====================
// 表单引用
const queryFormRef = ref<FormInstance>();
const formRef = ref<FormInstance>();
// 字典数据(从store获取或本地定义)
const { sys_order_status } = proxy.useDict('sys_order_status');
// ==================== 3. 计算属性 ====================
// 示例:计算已选中的订单数量
const selectedCount = computed(() => ids.value.length);
// ==================== 4. 方法定义 ====================
/** 查询订单列表 */
const getList = async () => {
loading.value = true;
try {
const res = await listOrder(queryParams);
orderList.value = res.rows;
total.value = res.total;
} finally {
loading.value = false;
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: SysOrderVo[]) => {
ids.value = selection.map(item => item.orderId);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
open.value = true;
title.value = '添加订单';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: SysOrderVo) => {
reset();
const orderId = row?.orderId || ids.value[0];
try {
const res = await getOrder(orderId);
Object.assign(form, res.data);
open.value = true;
title.value = '修改订单';
} catch (error) {
proxy.$modal.msgError('获取订单详情失败');
}
};
/** 提交按钮 */
const submitForm = async () => {
const valid = await formRef.value?.validate();
if (!valid) return;
try {
if (form.orderId) {
await updateOrder(form);
proxy.$modal.msgSuccess('修改成功');
} else {
await addOrder(form);
proxy.$modal.msgSuccess('新增成功');
}
open.value = false;
await getList();
} catch (error) {
proxy.$modal.msgError('操作失败');
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: SysOrderVo) => {
const orderIds = row?.orderId ? [row.orderId] : ids.value;
try {
await proxy.$modal.confirm(`是否确认删除订单编号为"${orderIds}"的数据项?`);
await delOrder(orderIds);
proxy.$modal.msgSuccess('删除成功');
await getList();
} catch {}
};
/** 取消按钮 */
const cancel = () => {
open.value = false;
reset();
};
/** 表单重置 */
const reset = () => {
Object.assign(form, {
orderId: undefined,
orderNo: '',
amount: 0,
status: '0',
remark: ''
});
formRef.value?.resetFields();
};
// ==================== 5. 生命周期钩子 ====================
onMounted(() => {
getList();
});
// ==================== 6. 暴露给模板的方法(可选)====================
// defineExpose({
// getList
// });
</script>
<style scoped lang="scss">
// 组件样式
</style>
组件封装示例(子组件):
<template>
<el-dialog
:title="title"
v-model="dialogVisible"
width="500px"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="订单编号" prop="orderNo">
<el-input v-model="formData.orderNo" placeholder="请输入订单编号" />
</el-form-item>
<el-form-item label="订单金额" prop="amount">
<el-input-number v-model="formData.amount" :min="0" :precision="2" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="OrderDialog">
import { ref, reactive, watch } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
// ==================== Props 定义 ====================
interface Props {
visible: boolean;
orderId?: number;
title?: string;
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
title: '订单详情'
});
// ==================== Emits 定义 ====================
interface Emits {
(e: 'update:visible', value: boolean): void;
(e: 'confirm', data: any): void;
}
const emit = defineEmits<Emits>();
// ==================== 响应式数据 ====================
const formRef = ref<FormInstance>();
const dialogVisible = ref(props.visible);
const formData = reactive({
orderNo: '',
amount: 0
});
const rules = reactive<FormRules>({
orderNo: [{ required: true, message: '请输入订单编号', trigger: 'blur' }],
amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }]
});
// ==================== 侦听器 ====================
// 侦听 props.visible 变化,同步到内部状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal;
});
// 侦听内部状态变化,同步到父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal);
});
// ==================== 方法 ====================
const handleConfirm = async () => {
const valid = await formRef.value?.validate();
if (valid) {
emit('confirm', { ...formData });
handleClose();
}
};
const handleCancel = () => {
handleClose();
};
const handleClose = () => {
dialogVisible.value = false;
formRef.value?.resetFields();
};
// ==================== 暴露方法给父组件 ====================
defineExpose({
// 父组件可以通过 ref 调用这些方法
resetForm: () => formRef.value?.resetFields()
});
</script>
使用子组件示例:
<template>
<div>
<el-button @click="openDialog">打开对话框</el-button>
<!-- 使用 v-model:visible 双向绑定 -->
<OrderDialog
ref="orderDialogRef"
v-model:visible="dialogVisible"
:order-id="currentOrderId"
title="编辑订单"
@confirm="handleDialogConfirm"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import OrderDialog from './components/OrderDialog.vue';
const dialogVisible = ref(false);
const currentOrderId = ref<number>();
const orderDialogRef = ref<InstanceType<typeof OrderDialog>>();
const openDialog = () => {
currentOrderId.value = 123;
dialogVisible.value = true;
};
const handleDialogConfirm = (data: any) => {
console.log('接收到的数据:', data);
// 处理业务逻辑
};
// 调用子组件暴露的方法
const resetDialog = () => {
orderDialogRef.value?.resetForm();
};
</script>
规范3:MyBatis-Plus 使用规范
核心原则:
- 优先使用 MyBatis-Plus 提供的 CRUD 方法,减少 XML 配置
- 复杂查询使用 LambdaQueryWrapper,避免字符串拼接
- 批量操作使用批处理方法,提升性能
- 自定义 SQL 放在 Mapper.xml 中,保持代码整洁
常用查询方法:
// 1. 基础查询
SysOrder order = orderMapper.selectById(orderId);
List<SysOrder> list = orderMapper.selectList(null);
// 2. 条件查询(LambdaQueryWrapper - 类型安全)
LambdaQueryWrapper<SysOrder> wrapper = Wrappers.lambdaQuery();
wrapper.eq(SysOrder::getOrderNo, "ORD123") // 等于
.like(SysOrder::getCustomerName, "张三") // 模糊查询
.between(SysOrder::getAmount, 100, 1000) // 区间查询
.in(SysOrder::getStatus, Arrays.asList(1, 2)) // IN 查询
.isNotNull(SysOrder::getCreateTime) // 非空判断
.orderByDesc(SysOrder::getCreateTime); // 排序
List<SysOrder> orders = orderMapper.selectList(wrapper);
// 3. 动态条件查询(根据条件拼接)
wrapper.like(StringUtils.isNotBlank(orderNo), SysOrder::getOrderNo, orderNo)
.eq(status != null, SysOrder::getStatus, status)
.ge(beginTime != null, SysOrder::getCreateTime, beginTime)
.le(endTime != null, SysOrder::getCreateTime, endTime);
// 4. 分页查询
Page<SysOrder> page = new Page<>(pageNum, pageSize);
IPage<SysOrder> result = orderMapper.selectPage(page, wrapper);
// 5. 只查询特定字段(select)
wrapper.select(SysOrder::getOrderId, SysOrder::getOrderNo, SysOrder::getAmount);
// 6. 统计查询
Long count = orderMapper.selectCount(wrapper);
增删改操作:
// 1. 新增
SysOrder order = new SysOrder();
order.setOrderNo("ORD123");
order.setAmount(new BigDecimal("999.99"));
orderMapper.insert(order); // 返回影响行数,主键会自动回填到 order 对象
// 2. 修改(根据ID)
order.setStatus(2);
orderMapper.updateById(order); // 只更新非null字段
// 3. 条件修改
LambdaUpdateWrapper<SysOrder> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.set(SysOrder::getStatus, 2)
.eq(SysOrder::getOrderNo, "ORD123");
orderMapper.update(null, updateWrapper);
// 4. 删除(根据ID)
orderMapper.deleteById(orderId);
// 5. 批量删除
orderMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));
// 6. 条件删除
LambdaQueryWrapper<SysOrder> deleteWrapper = Wrappers.lambdaQuery();
deleteWrapper.eq(SysOrder::getStatus, 0);
orderMapper.delete(deleteWrapper);
批量操作(高性能):
// 批量插入(推荐使用 Service 的批量方法)
List<SysOrder> orderList = new ArrayList<>();
// ... 填充数据
orderService.saveBatch(orderList); // 默认批次大小 1000
// 批量更新
orderService.updateBatchById(orderList);
// 自定义批次大小
orderService.saveBatch(orderList, 500); // 每批 500 条
自定义 SQL(Mapper.xml):
<!-- SysOrderMapper.xml -->
<mapper namespace="com.ruoyi.system.mapper.SysOrderMapper">
<!-- 结果映射 -->
<resultMap id="SysOrderResult" type="SysOrder">
<id property="orderId" column="order_id" />
<result property="orderNo" column="order_no" />
<result property="amount" column="amount" />
<result property="status" column="status" />
<result property="createTime" column="create_time" />
</resultMap>
<!-- 复杂查询:订单关联商品信息 -->
<select id="selectOrderWithProducts" resultType="SysOrderVo">
SELECT
o.order_id,
o.order_no,
o.amount,
GROUP_CONCAT(p.product_name) as product_names
FROM sys_order o
LEFT JOIN sys_order_product op ON o.order_id = op.order_id
LEFT JOIN sys_product p ON op.product_id = p.product_id
WHERE o.del_flag = '0'
<if test="orderNo != null and orderNo != ''">
AND o.order_no LIKE CONCAT('%', #{orderNo}, '%')
</if>
GROUP BY o.order_id
ORDER BY o.create_time DESC
</select>
<!-- 批量插入(MySQL) -->
<insert id="insertBatch">
INSERT INTO sys_order (order_no, amount, status, create_time)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.orderNo}, #{item.amount}, #{item.status}, #{item.createTime})
</foreach>
</insert>
</mapper>
Mapper 接口定义:
@Mapper
public interface SysOrderMapper extends BaseMapper<SysOrder> {
/**
* 查询订单及关联商品信息
*/
List<SysOrderVo> selectOrderWithProducts(@Param("orderNo") String orderNo);
/**
* 批量插入订单
*/
int insertBatch(@Param("list") List<SysOrder> orderList);
}
规范4:事务管理规范
事务注解使用:
@Service
public class SysOrderServiceImpl implements ISysOrderService {
/**
* 事务方法(标准写法)
* rollbackFor: 指定哪些异常触发回滚(必须指定 Exception.class)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean createOrder(SysOrderBo bo) {
// 1. 插入订单主表
SysOrder order = BeanUtil.copyProperties(bo, SysOrder.class);
orderMapper.insert(order);
// 2. 插入订单明细
List<OrderDetail> details = bo.getDetails();
for (OrderDetail detail : details) {
detail.setOrderId(order.getOrderId());
orderDetailMapper.insert(detail);
}
// 3. 扣减库存
for (OrderDetail detail : details) {
productService.reduceStock(detail.getProductId(), detail.getQuantity());
}
// 如果任何步骤抛出异常,所有操作都会回滚
return true;
}
/**
* 只读事务(查询优化)
*/
@Override
@Transactional(readOnly = true)
public List<SysOrderVo> selectOrderList(SysOrderBo bo) {
// 只读事务可以提升查询性能
return orderMapper.selectOrderList(bo);
}
}
事务传播行为:
// REQUIRED(默认):如果当前存在事务则加入,否则新建事务
@Transactional(propagation = Propagation.REQUIRED)
// REQUIRES_NEW:总是新建事务,挂起当前事务(用于日志记录)
@Transactional(propagation = Propagation.REQUIRES_NEW)
// NESTED:嵌套事务,子事务回滚不影响父事务
@Transactional(propagation = Propagation.NESTED)
规范5:异常处理与日志规范
统一异常处理:
@Service
public class SysOrderServiceImpl implements ISysOrderService {
private static final Logger log = LoggerFactory.getLogger(SysOrderServiceImpl.class);
@Override
public boolean insertOrder(SysOrder order) {
try {
// 业务逻辑
orderMapper.insert(order);
return true;
} catch (DuplicateKeyException e) {
// 捕获特定异常,抛出业务异常
log.error("订单号重复: {}", order.getOrderNo(), e);
throw new ServiceException("订单号已存在");
} catch (Exception e) {
// 记录日志
log.error("创建订单失败", e);
throw new ServiceException("创建订单失败,请联系管理员");
}
}
}
Controller 层异常处理(全局异常处理器已配置):
@RestController
public class SysOrderController {
@PostMapping
public AjaxResult add(@Validated @RequestBody SysOrderBo bo) {
// 不需要 try-catch,全局异常处理器会统一处理
// 校验失败会自动返回 400 错误
// ServiceException 会自动返回友好的错误信息
return toAjax(orderService.insertOrder(order));
}
}
日志记录规范:
// 1. 使用 Slf4j 注解(推荐)
@Slf4j
@Service
public class SysOrderServiceImpl {
public void processOrder(Long orderId) {
log.debug("开始处理订单: {}", orderId); // 调试信息
log.info("订单处理成功: {}", orderId); // 一般信息
log.warn("订单库存不足: {}", orderId); // 警告信息
log.error("订单处理失败: {}", orderId, ex); // 错误信息(带异常堆栈)
}
}
// 2. 使用操作日志注解(记录到数据库)
@Log(title = "订单管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysOrderBo bo) {
// 会自动记录操作人、操作时间、操作模块、请求参数等
return toAjax(orderService.insertOrder(order));
}
规范6:参数校验规范
DTO/Bo 对象校验:
import javax.validation.constraints.*;
public class SysOrderBo {
/** 订单ID(修改时必填) */
@NotNull(message = "订单ID不能为空", groups = {EditGroup.class})
private Long orderId;
/** 订单编号(必填,最长50字符) */
@NotBlank(message = "订单编号不能为空")
@Size(max = 50, message = "订单编号长度不能超过50个字符")
private String orderNo;
/** 订单金额(必填,最小0.01,最大99999.99) */
@NotNull(message = "订单金额不能为空")
@DecimalMin(value = "0.01", message = "订单金额必须大于0")
@DecimalMax(value = "99999.99", message = "订单金额不能超过99999.99")
private BigDecimal amount;
/** 客户手机号(必填,手机号格式) */
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
/** 邮箱(可选,邮箱格式) */
@Email(message = "邮箱格式不正确")
private String email;
/** 订单明细(至少1项) */
@NotEmpty(message = "订单明细不能为空")
@Valid // 级联校验
private List<OrderDetailBo> details;
}
Controller 层校验:
// 1. 使用 @Validated 触发校验
@PostMapping
public AjaxResult add(@Validated @RequestBody SysOrderBo bo) {
// 校验失败会自动返回 400 错误和错误信息
return toAjax(orderService.insertOrder(order));
}
// 2. 分组校验(新增和修改使用不同的校验规则)
@PostMapping
public AjaxResult add(@Validated(AddGroup.class) @RequestBody SysOrderBo bo) {
return toAjax(orderService.insertOrder(order));
}
@PutMapping
public AjaxResult edit(@Validated(EditGroup.class) @RequestBody SysOrderBo bo) {
return toAjax(orderService.updateOrder(order));
}
// 3. 路径参数校验
@GetMapping("/{orderId}")
public AjaxResult getInfo(@PathVariable @Min(value = 1, message = "订单ID必须大于0") Long orderId) {
return success(orderService.selectOrderById(orderId));
}
自定义校验注解:
// 自定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = OrderStatusValidator.class)
public @interface ValidOrderStatus {
String message() default "订单状态值不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 校验器实现
public class OrderStatusValidator implements ConstraintValidator<ValidOrderStatus, Integer> {
private static final Set<Integer> VALID_STATUS = Set.of(0, 1, 2, 3, 4);
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) {
return true; // null 值由 @NotNull 校验
}
return VALID_STATUS.contains(value);
}
}
// 使用
public class SysOrderBo {
@ValidOrderStatus(message = "订单状态只能是0-4之间的值")
private Integer status;
}
## 禁止事项
### 后端开发禁止事项
1. **❌ 对象流转违规**
- 禁止在Controller层直接返回数据库实体`Entity`,必须使用`Vo`进行数据裁剪
- 禁止Controller直接操作Mapper层,必须通过Service层处理业务逻辑
- 禁止在Entity中添加非数据库字段(应放在Vo中)
- 禁止手写大量的setter/getter进行对象拷贝,必须使用`BeanUtil.copyProperties`或MapStruct
2. **❌ Service层违规**
- 禁止在Service层中直接使用`HttpServletRequest`获取参数,必须通过Controller层传递
- 禁止在Service层返回`AjaxResult`,应返回业务对象或boolean
- 禁止Service方法抛出不明确的`Exception`,应抛出`ServiceException`并附带友好错误信息
- 禁止在无事务方法中进行多表操作,必须添加`@Transactional`注解
3. **❌ MyBatis-Plus违规**
- 禁止使用字符串拼接SQL(容易SQL注入),必须使用LambdaQueryWrapper
- 禁止在循环中执行单条insert/update(性能低下),应使用批量方法
- 禁止直接使用`selectList(null)`查询全表(数据量大会OOM),必须添加分页或限制条件
- 禁止在Entity上使用`@TableField(exist = false)`定义过多非数据库字段
4. **❌ 事务管理违规**
- 禁止忘记添加`rollbackFor = Exception.class`(默认只回滚RuntimeException)
- 禁止在事务方法中捕获异常后不抛出(会导致事务不回滚)
- 禁止在Controller层方法上添加`@Transactional`(应在Service层)
- 禁止事务方法调用同类中的另一个事务方法(事务会失效,应使用AOP代理)
5. **❌ 异常处理违规**
- 禁止捕获异常后只打印日志不处理(`e.printStackTrace()`或空catch块)
- 禁止抛出Exception基类,应抛出具体异常(`ServiceException`、`BusinessException`等)
- 禁止在finally块中使用return(会覆盖try块的返回值和异常)
- 禁止使用`System.out.println`打印日志,必须使用Logger
6. **❌ 日志记录违规**
- 禁止在生产代码中使用`System.out.println`,必须使用Slf4j
- 禁止使用字符串拼接记录日志(`log.info("用户:" + username)`),应使用占位符(`log.info("用户: {}", username)`)
- 禁止在高频调用的方法中使用`log.info`(应使用`log.debug`)
- 禁止记录敏感信息(密码、身份证号、银行卡号等)到日志
7. **❌ 安全违规**
- 禁止在Vo中返回密码、盐值等敏感字段
- 禁止在日志中打印完整的用户敏感信息
- 禁止在前端直接传递SQL语句或表名
- 禁止使用字符串拼接构造SQL(必须使用参数化查询)
8. **❌ 性能违规**
- 禁止在循环中调用数据库(N+1查询问题)
- 禁止查询大量数据不分页(应使用`PageHelper.startPage()`)
- 禁止在MyBatis的`<select>`中使用`SELECT *`(应明确指定字段)
- 禁止在高并发接口中使用synchronized同步代码块(应使用分布式锁或Redis)
### 前端开发禁止事项
1. **❌ Vue3语法违规**
- 禁止在Vue3中继续使用Options API(`export default {}`),必须使用`<script setup>`
- 禁止直接修改props(props是只读的),应通过emit通知父组件修改
- 禁止解构reactive对象而不使用`toRefs`(会丢失响应性)
- 禁止在模板中使用复杂的表达式(应使用computed计算属性)
2. **❌ 响应式数据违规**
- 禁止使用`var`声明变量,必须使用`const`或`let`
- 禁止直接给reactive对象赋值(`queryParams = {}`),应使用`Object.assign`或单独修改属性
- 禁止在ref对象上忘记使用`.value`(模板中会自动解包,但JS代码中需要)
- 禁止过度使用`any`类型,应明确定义TypeScript接口
3. **❌ API调用违规**
- 禁止在前端API调用中使用硬编码的URL,必须统一在`@/api/xxx`中定义
- 禁止不处理API异常(应使用try-catch或全局错误处理)
- 禁止在组件中直接调用axios,必须封装成API方法
- 禁止同步调用API(应使用async/await或Promise)
4. **❌ 组件设计违规**
- 禁止在一个组件中编写超过500行代码(应拆分成多个子组件)
- 禁止在子组件中直接修改父组件的数据(应使用emit事件)
- 禁止过度使用`provide/inject`(应优先使用props和emit)
- 禁止在组件中直接操作DOM(应使用ref和Vue的响应式系统)
5. **❌ 表单处理违规**
- 禁止不校验表单就提交(应使用`formRef.value?.validate()`)
- 禁止表单提交后不重置表单(应调用`resetFields()`)
- 禁止在v-model上绑定非响应式数据
- 禁止使用字符串作为表单字段名(应使用对象的属性名)
6. **❌ 性能优化违规**
- 禁止在computed中执行异步操作(应使用watch或watchEffect)
- 禁止在template中频繁调用方法(应使用computed缓存结果)
- 禁止不使用key绑定v-for循环(会导致渲染错误)
- 禁止在高频触发的事件中不使用防抖/节流(如input、scroll)
7. **❌ TypeScript违规**
- 禁止滥用`as any`强制类型转换(会失去类型检查)
- 禁止不定义API响应的接口类型(应在`types.ts`中定义)
- 禁止使用隐式any(应开启`strict`模式)
- 禁止在接口中使用可选属性而不提供默认值(容易出现undefined错误)
8. **❌ 调试代码违规**
- 禁止在生产代码中保留`console.log`(应在构建时移除或使用环境变量控制)
- 禁止在生产代码中保留`debugger`语句
- 禁止提交注释掉的大段代码(应使用Git版本控制)
- 禁止在代码中硬编码测试数据
## 参考代码与最佳实践
### 若依框架参考文件
#### 后端参考文件
- **Controller层示例**:`ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java`
- 标准的CRUD操作示例
- 权限注解使用
- 日志记录注解使用
- 分页查询处理
- **Service层示例**:`ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java`
- 业务逻辑处理
- 事务管理
- 对象转换
- 异常处理
- **Mapper层示例**:`ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java`
- MyBatis-Plus基础用法
- 自定义SQL方法
- **实体类示例**:
- Entity: `ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java`
- Bo: `ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysUserBo.java`
- Vo: `ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserVo.java`
- **工具类**:
- BeanUtil: `ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtil.java`
- StringUtils: `ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java`
- SecurityUtils: `ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java`
#### 前端参考文件
- **Vue3页面示例**:`ruoyi-ui/src/views/system/user/index.vue`
- 完整的CRUD页面
- 表格、表单、对话框组件使用
- 分页处理
- 权限按钮控制
- **API封装示例**:`ruoyi-ui/src/api/system/user.js`
- 统一的API接口定义
- 请求参数封装
- 响应数据处理
- **类型定义示例**:`ruoyi-ui/src/api/system/user/types.ts`
- TypeScript接口定义
- 请求/响应类型
- **组件封装示例**:
- 字典标签: `ruoyi-ui/src/components/DictTag/index.vue`
- 分页组件: `ruoyi-ui/src/components/Pagination/index.vue`
- 文件上传: `ruoyi-ui/src/components/FileUpload/index.vue`
### 命名规范
#### 后端命名规范
```java
// 1. 包名:全小写,使用点分隔
com.ruoyi.system.controller
com.ruoyi.system.service.impl
// 2. 类名:大驼峰(PascalCase)
public class SysUserController {}
public class SysUserServiceImpl {}
// 3. 方法名:小驼峰(camelCase)
public List<SysUserVo> selectUserList() {}
public boolean insertUser(SysUser user) {}
// 4. 变量名:小驼峰
private String userName;
private List<SysUser> userList;
// 5. 常量名:全大写,下划线分隔
public static final String USER_STATUS_NORMAL = "0";
public static final int MAX_PAGE_SIZE = 1000;
// 6. 实体类字段:小驼峰(数据库字段使用下划线)
private String userName; // 对应数据库字段 user_name
private Long userId; // 对应数据库字段 user_id
// 7. Boolean类型:使用 is/has/can 前缀
private Boolean isAdmin;
private Boolean hasPermission;
// 8. 集合类型:使用复数或加上 List/Map 后缀
private List<SysUser> userList;
private Map<String, Object> userMap;
前端命名规范
// 1. 文件名:kebab-case(短横线命名)
user-list.vue
order-detail.vue
sys-user.ts
// 2. 组件名:PascalCase(大驼峰)
<script setup lang="ts" name="UserList">
export default {
name: 'OrderDetail'
}
// 3. 变量名和方法名:camelCase(小驼峰)
const userName = ref('');
const handleQuery = () => {};
// 4. 常量名:SCREAMING_SNAKE_CASE(全大写下划线)
const MAX_UPLOAD_SIZE = 10 * 1024 * 1024;
const API_BASE_URL = 'http://api.example.com';
// 5. 接口/类型:PascalCase
interface UserInfo {
userId: number;
userName: string;
}
type UserStatus = 0 | 1 | 2;
// 6. 枚举:PascalCase(枚举值全大写)
enum OrderStatus {
PENDING = 0,
PAID = 1,
SHIPPED = 2,
COMPLETED = 3
}
// 7. CSS类名:kebab-case
.user-list-container {}
.order-detail-header {}
// 8. 事件处理方法:handle开头
const handleClick = () => {};
const handleSubmit = () => {};
const handleDelete = () => {};
代码注释规范
后端注释规范
/**
* 订单管理Controller
*
* @author ruoyi
* @date 2024-01-20
*/
@RestController
@RequestMapping("/system/order")
public class SysOrderController extends BaseController {
/**
* 查询订单列表
*
* @param bo 查询条件
* @return 订单列表(分页)
*/
@GetMapping("/list")
public TableDataInfo list(SysOrderBo bo) {
startPage();
List<SysOrderVo> list = orderService.selectOrderList(bo);
return getDataTable(list);
}
/**
* 新增订单
*
* 业务流程:
* 1. 校验订单号是否重复
* 2. 创建订单主表记录
* 3. 创建订单明细记录
* 4. 扣减商品库存
*
* @param bo 订单业务对象
* @return 操作结果
*/
@PostMapping
public AjaxResult add(@Validated @RequestBody SysOrderBo bo) {
return toAjax(orderService.insertOrder(bo));
}
}
前端注释规范
/**
* 订单管理页面
*/
<script setup lang="ts" name="OrderList">
/**
* 查询订单列表
* @description 根据查询条件获取订单列表,支持分页
*/
const getList = async () => {
loading.value = true;
try {
const res = await listOrder(queryParams);
orderList.value = res.rows;
total.value = res.total;
} finally {
loading.value = false;
}
};
/**
* 处理表格多选变化
* @param selection 选中的行数据
*/
const handleSelectionChange = (selection: SysOrderVo[]) => {
ids.value = selection.map(item => item.orderId);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
// TODO: 待实现订单导出功能
// FIXME: 修复订单状态更新后不刷新列表的问题
// NOTE: 此处使用防抖避免频繁调用接口
</script>
文件结构规范
后端模块结构
ruoyi-system/
├── src/main/java/com/ruoyi/system/
│ ├── controller/ # 控制器层
│ │ └── SysOrderController.java
│ ├── service/ # 服务接口层
│ │ ├── ISysOrderService.java
│ │ └── impl/ # 服务实现层
│ │ └── SysOrderServiceImpl.java
│ ├── mapper/ # 数据访问层
│ │ └── SysOrderMapper.java
│ ├── domain/ # 领域模型层
│ │ ├── SysOrder.java # 实体类
│ │ ├── bo/ # 业务对象
│ │ │ └── SysOrderBo.java
│ │ └── vo/ # 视图对象
│ │ └── SysOrderVo.java
│ └── enums/ # 枚举类
│ └── OrderStatusEnum.java
└── src/main/resources/
└── mapper/system/ # MyBatis XML
└── SysOrderMapper.xml
前端模块结构
ruoyi-ui/
├── src/
│ ├── views/system/order/ # 页面目录
│ │ ├── index.vue # 主页面
│ │ ├── components/ # 页面专用组件
│ │ │ ├── OrderDialog.vue
│ │ │ └── OrderDetail.vue
│ │ └── types.ts # 页面类型定义(可选)
│ ├── api/system/ # API接口
│ │ └── order/
│ │ ├── index.ts # API方法
│ │ └── types.ts # 接口类型定义
│ ├── components/ # 全局公共组件
│ │ ├── DictTag/
│ │ └── Pagination/
│ ├── utils/ # 工具函数
│ │ ├── request.ts # axios封装
│ │ └── validate.ts # 校验函数
│ └── stores/ # Pinia状态管理
│ └── modules/
│ └── order.ts
代码质量检查清单
提交代码前必须检查的项目
后端代码检查清单
分层架构检查
- Controller只负责接收请求和响应,没有业务逻辑
- Service层包含所有业务逻辑
- Mapper层只负责数据库操作
- 对象流转遵循:Bo → Entity → Vo
对象转换检查
- Controller返回的是Vo,不是Entity
- 使用了BeanUtil.copyProperties或MapStruct进行对象拷贝
- 没有在Entity中添加非数据库字段
- Vo中不包含敏感字段(密码、盐值等)
数据库操作检查
- 使用了LambdaQueryWrapper,没有字符串拼接SQL
- 批量操作使用了批量方法(saveBatch/updateBatchById)
- 查询大量数据时使用了分页
- 没有在循环中执行数据库操作
事务管理检查
- Service层的数据修改方法添加了@Transactional注解
- 包含了rollbackFor = Exception.class
- 没有在Controller层使用@Transactional
- 捕获异常后重新抛出,避免事务失效
异常处理检查
- 使用了try-catch捕获异常
- 抛出了具体的业务异常(ServiceException)
- 异常信息对用户友好
- 记录了必要的错误日志
日志记录检查
- 使用了Slf4j,没有System.out.println
- 日志使用占位符{},没有字符串拼接
- 没有记录敏感信息
- 日志级别使用正确(debug/info/warn/error)
参数校验检查
- Bo对象添加了校验注解(@NotNull、@NotBlank等)
- Controller方法使用了@Validated触发校验
- 重要参数进行了非空判断
- 数值范围进行了校验
安全检查
- Controller方法添加了权限注解(@PreAuthorize)
- SQL使用参数化查询,没有拼接
- 敏感操作添加了操作日志(@Log)
- 返回的Vo不包含敏感信息
代码规范检查
- 类、方法、变量命名符合规范
- 添加了必要的注释(类注释、方法注释)
- 没有大段注释掉的代码
- 导入的包没有使用通配符(import xxx.*)
性能检查
- 没有N+1查询问题
- 查询只返回需要的字段,没有SELECT *
- 高频调用的方法考虑了缓存
- 大数据量处理使用了分批处理
前端代码检查清单
Vue3语法检查
- 使用了
- 使用了
Decide Fit First
Design Intent
How To Use It
Boundaries And Review