feat: 新增编码预览功能

- 后端新增 /rpc-api/base/code-gen/preview 接口
- SequenceAllocator.preview 查询序号但不消耗
- 前端操作列新增预览按钮,弹窗展示下一个编码
This commit is contained in:
zhouz
2026-07-15 18:14:40 +08:00
parent f3ef75f990
commit fbc5554c6a
7 changed files with 125 additions and 3 deletions

View File

@@ -9,6 +9,8 @@ import jakarta.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* RPC 服务 - 编码生成
@@ -31,4 +33,8 @@ public interface CodeGenApi {
@Operation(summary = "生成编码")
CommonResult<String> generate(@Valid @RequestBody CodeGenerateReqDTO reqDTO);
@GetMapping(PREFIX + "/preview")
@Operation(summary = "预览下一个编码(不消耗序号)")
CommonResult<String> preview(@RequestParam("ruleCode") String ruleCode);
}

View File

@@ -8,9 +8,11 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static cn.code.nl.framework.common.pojo.CommonResult.success;
@@ -39,4 +41,11 @@ public class CodeGenController {
return success(code);
}
@GetMapping("/preview")
@Operation(summary = "预览下一个编码(不消耗序号)")
public CommonResult<String> preview(@RequestParam("ruleCode") String ruleCode) {
String code = codeGenService.preview(ruleCode);
return success(code);
}
}

View File

@@ -15,4 +15,12 @@ public interface CodeGenService {
*/
String generate(String ruleCode);
/**
* 预览下一个编码(不消耗序号)
*
* @param ruleCode 规则编码
* @return 预览的编码
*/
String preview(String ruleCode);
}

View File

@@ -54,6 +54,26 @@ public class CodeGenServiceImpl implements CodeGenService {
return postProcess(sb.toString(), rule);
}
@Override
public String preview(String ruleCode) {
// 1. 获取规则配置
CodeRuleDO rule = codeRuleService.getRuleByCode(ruleCode);
// 2. 遍历分段SEQUENCE 段用 preview不消耗序号
List<SegmentConfig> segments = rule.getSegmentConfig();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < segments.size(); i++) {
String segmentValue = buildPreviewSegment(rule, segments.get(i));
sb.append(segmentValue);
if (i < segments.size() - 1 && StrUtil.isNotBlank(rule.getSeparator())) {
sb.append(rule.getSeparator());
}
}
// 3. 后处理
return postProcess(sb.toString(), rule);
}
/**
* 根据段类型分发处理,生成段值
*/
@@ -66,6 +86,18 @@ public class CodeGenServiceImpl implements CodeGenService {
};
}
/**
* 预览模式分发SEQUENCE 段不消耗序号)
*/
private String buildPreviewSegment(CodeRuleDO rule, SegmentConfig segment) {
return switch (segment.getType()) {
case "PREFIX" -> buildPrefixSegment(segment);
case "DATE" -> buildDateSegment(segment);
case "SEQUENCE" -> sequenceAllocator.preview(rule.getId(), segment);
default -> throw new IllegalArgumentException("不支持的段类型: " + segment.getType());
};
}
/**
* 生成固定前缀段
*/

View File

@@ -189,4 +189,19 @@ public class SequenceAllocator {
return paddingChar.repeat(paddingLen - raw.length()) + raw;
}
/**
* 预览下一个序号(不实际分配,不更新数据库)
*
* @param ruleId 规则ID
* @param segment 序号段配置
* @return 格式化后的序号字符串
*/
public String preview(Long ruleId, SegmentConfig segment) {
String resetKey = buildResetKey(segment.getResetBy());
CodeSequenceDO seq = codeSequenceMapper.selectByRuleIdAndResetKey(ruleId, resetKey);
Long startAt = segment.getStartAt() != null ? segment.getStartAt() : 1L;
long nextValue = (seq != null) ? seq.getCurrentValue() + 1 : startAt;
return formatSequence(nextValue, segment);
}
}

View File

@@ -79,3 +79,11 @@ export async function updateCodeRule(data: BaseCodeRuleApi.CodeRule) {
export async function deleteCodeRule(id: number) {
return requestClient.delete(`/base/code-rule/delete?id=${id}`);
}
/** 预览下一个编码(不消耗序号) */
export async function previewCode(ruleCode: string) {
return requestClient.get<string>(
'/rpc-api/base/code-gen/preview',
{ params: { ruleCode } },
);
}

View File

@@ -7,10 +7,10 @@ import { ref } from 'vue';
import { confirm, Page, useVbenModal } from '@vben/common-ui';
import { isEmpty } from '@vben/utils';
import { message } from 'antdv-next';
import { Modal, message } from 'antdv-next';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteCodeRule, getCodeRulePage } from '#/api/base/codegen';
import { deleteCodeRule, getCodeRulePage, previewCode } from '#/api/base/codegen';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
@@ -51,7 +51,7 @@ async function handleDelete(row: BaseCodeRuleApi.CodeRule) {
}
}
/** 批量删除编码规则 */
/** 批量删除 */
async function handleDeleteBatch() {
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
const hideLoading = message.loading({
@@ -70,6 +70,25 @@ async function handleDeleteBatch() {
}
}
/** 预览下一个编码 */
const previewVisible = ref(false);
const previewLoading = ref(false);
const previewCodeValue = ref('');
const previewTitle = ref('');
async function handlePreview(row: BaseCodeRuleApi.CodeRule) {
previewTitle.value = row.ruleName;
previewLoading.value = true;
previewVisible.value = true;
try {
const result = await previewCode(row.ruleCode);
previewCodeValue.value = result ?? '';
} catch {
previewCodeValue.value = '预览失败';
} finally {
previewLoading.value = false;
}
}
const checkedIds = ref<number[]>([]);
function handleRowCheckboxChange({
records,
@@ -145,6 +164,13 @@ const [Grid, gridApi] = useVbenVxeGrid({
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '预览',
type: 'link',
icon: ACTION_ICON.SEARCH,
auth: ['base:code-rule:query'],
onClick: handlePreview.bind(null, row),
},
{
label: $t('common.edit'),
type: 'link',
@@ -167,5 +193,23 @@ const [Grid, gridApi] = useVbenVxeGrid({
/>
</template>
</Grid>
<!-- 预览弹窗 -->
<Modal
v-model:open="previewVisible"
title="编码预览"
:footer="null"
:width="500"
>
<div v-if="previewLoading" class="py-8 text-center text-gray-400">
加载中...
</div>
<div v-else class="py-4">
<p class="mb-2 text-sm text-gray-500">规则{{ previewTitle }}</p>
<p class="text-2xl font-mono font-bold tracking-wider text-blue-600">
{{ previewCodeValue }}
</p>
</div>
</Modal>
</Page>
</template>