Add executable business blueprint workflow

This commit is contained in:
王鹏
2026-05-24 13:29:42 +08:00
parent ecdc015c2f
commit 6682a44a43
40 changed files with 4756 additions and 113 deletions

View File

@@ -280,6 +280,9 @@ public interface I${ClassName}Service
(910108, 9101, 'serviceImpl.java.vm', NULL, 'serviceImpl.java.vm',
'package ${packageName}.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
@@ -374,6 +377,24 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
#foreach($field in $action.requestFields)
requireBusinessParam(params, "${field}");
#end
#end
#if($action.ruleChecks)
#foreach($rule in $action.ruleChecks)
#set($conditionFields = $rule.conditionFields)
#if($rule.type == "EXISTS")
assertExists(params, "${rule.targetTable}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}");
#elseif($rule.type == "NOT_EXISTS")
assertNotExists(params, "${rule.targetTable}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}");
#elseif($rule.type == "FIELD_EQUALS")
assertFieldEquals(params, "${rule.targetTable}", "${rule.targetField}", "${rule.expectedValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}");
#elseif($rule.type == "FIELD_NOT_EQUALS")
assertFieldNotEquals(params, "${rule.targetTable}", "${rule.targetField}", "${rule.expectedValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}");
#elseif($rule.type == "FIELD_IN")
assertFieldIn(params, "${rule.targetTable}", "${rule.targetField}", new String[] {#foreach($value in $rule.expectedValues)"${value}"#if($foreach.hasNext), #end#end}, new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}");
#elseif($rule.type == "NUMBER_GTE")
assertNumberGte(params, "${rule.targetTable}", "${rule.targetField}", "${rule.compareValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${rule.message}");
#end
#end
#end
int rows = 0;
#if($action.effects)
@@ -384,6 +405,30 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
#set($conditionFields = $action.requestFields)
#end
rows += executeUpdateFieldEffect(params, "${effect.targetTable}", "${effect.targetField}", "${effect.value}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end});
#elseif($effect.type == "INSERT_ROW")
#set($effectIndex = $foreach.index)
Map<String, Object> insertValues${effectIndex} = new java.util.LinkedHashMap<String, Object>();
#foreach($entry in $effect.values.entrySet())
insertValues${effectIndex}.put("${entry.key}", resolveBusinessValue(params, "${entry.value}"));
#end
rows += insertRow("${effect.targetTable}", insertValues${effectIndex});
#elseif($effect.type == "UPDATE_FIELDS")
#set($conditionFields = $effect.conditionFields)
#set($effectIndex = $foreach.index)
Map<String, Object> updateValues${effectIndex} = new java.util.LinkedHashMap<String, Object>();
#foreach($entry in $effect.values.entrySet())
updateValues${effectIndex}.put("${entry.key}", resolveBusinessValue(params, "${entry.value}"));
#end
rows += updateFields(params, "${effect.targetTable}", updateValues${effectIndex}, new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end});
#elseif($effect.type == "SET_STATUS")
#set($conditionFields = $effect.conditionFields)
rows += setStatus(params, "${effect.targetTable}", "${effect.targetField}", resolveBusinessValue(params, "${effect.value}"), new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end});
#elseif($effect.type == "INCREASE_NUMBER")
#set($conditionFields = $effect.conditionFields)
rows += increaseNumberField(params, "${effect.targetTable}", "${effect.targetField}", "${effect.amount}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end});
#elseif($effect.type == "DECREASE_NUMBER")
#set($conditionFields = $effect.conditionFields)
rows += decreaseNumberField(params, "${effect.targetTable}", "${effect.targetField}", "${effect.amount}", "${effect.minValue}", new String[] {#foreach($field in $conditionFields)"${field}"#if($foreach.hasNext), #end#end}, "${effect.message}");
#else
// Unsupported effect type ${effect.type}: ${effect.description}
#end
@@ -400,6 +445,143 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
}
}
private void assertExists(Map<String, Object> params, String tableName, String[] conditionFields, String message)
{
if (countRows(params, tableName, conditionFields) <= 0) {
throw new ServiceException(defaultMessage(message, "Business record does not exist"));
}
}
private void assertNotExists(Map<String, Object> params, String tableName, String[] conditionFields, String message)
{
if (countRows(params, tableName, conditionFields) > 0) {
throw new ServiceException(defaultMessage(message, "Business record already exists"));
}
}
private void assertFieldEquals(Map<String, Object> params, String tableName, String targetField, String expectedValue,
String[] conditionFields, String message)
{
Object value = querySingleField(params, tableName, targetField, conditionFields);
if (value == null || !String.valueOf(resolveBusinessValue(params, expectedValue)).equals(String.valueOf(value))) {
throw new ServiceException(defaultMessage(message, "Business rule check failed"));
}
}
private void assertFieldNotEquals(Map<String, Object> params, String tableName, String targetField, String expectedValue,
String[] conditionFields, String message)
{
Object value = querySingleField(params, tableName, targetField, conditionFields);
if (value != null && String.valueOf(resolveBusinessValue(params, expectedValue)).equals(String.valueOf(value))) {
throw new ServiceException(defaultMessage(message, "Business rule check failed"));
}
}
private void assertFieldIn(Map<String, Object> params, String tableName, String targetField, String[] expectedValues,
String[] conditionFields, String message)
{
Object value = querySingleField(params, tableName, targetField, conditionFields);
String current = value == null ? "" : String.valueOf(value);
for (String expectedValue : expectedValues) {
if (String.valueOf(resolveBusinessValue(params, expectedValue)).equals(current)) {
return;
}
}
throw new ServiceException(defaultMessage(message, "Business rule check failed"));
}
private void assertNumberGte(Map<String, Object> params, String tableName, String targetField, String compareValue,
String[] conditionFields, String message)
{
Object value = querySingleField(params, tableName, targetField, conditionFields);
BigDecimal current = toBigDecimal(value, "Business numeric value is invalid");
BigDecimal expected = resolveBusinessNumber(params, compareValue);
if (current.compareTo(expected) < 0) {
throw new ServiceException(defaultMessage(message, "Business numeric rule check failed"));
}
}
private int insertRow(String tableName, Map<String, Object> values)
{
String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid");
if (values == null || values.isEmpty()) {
throw new ServiceException("Business insert values cannot be empty");
}
StringBuilder fields = new StringBuilder();
StringBuilder placeholders = new StringBuilder();
List<Object> args = new ArrayList<Object>();
for (Map.Entry<String, Object> entry : values.entrySet()) {
if (fields.length() > 0) {
fields.append(", ");
placeholders.append(", ");
}
fields.append(requireSafeIdentifier(entry.getKey(), "Business effect value field is invalid"));
placeholders.append("?");
args.add(entry.getValue());
}
return jdbcTemplate.update("insert into " + safeTableName + " (" + fields + ") values (" + placeholders + ")", args.toArray());
}
private int updateFields(Map<String, Object> params, String tableName, Map<String, Object> values, String[] conditionFields)
{
String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid");
if (values == null || values.isEmpty()) {
throw new ServiceException("Business update values cannot be empty");
}
StringBuilder sets = new StringBuilder();
List<Object> args = new ArrayList<Object>();
for (Map.Entry<String, Object> entry : values.entrySet()) {
if (sets.length() > 0) {
sets.append(", ");
}
sets.append(requireSafeIdentifier(entry.getKey(), "Business effect value field is invalid")).append(" = ?");
args.add(entry.getValue());
}
String whereClause = buildWhereClause(params, conditionFields);
appendConditionValues(args, params, conditionFields);
return jdbcTemplate.update("update " + safeTableName + " set " + sets + " where " + whereClause, args.toArray());
}
private int setStatus(Map<String, Object> params, String tableName, String targetField, Object value, String[] conditionFields)
{
Map<String, Object> values = new java.util.LinkedHashMap<String, Object>();
values.put(targetField, value);
return updateFields(params, tableName, values, conditionFields);
}
private int increaseNumberField(Map<String, Object> params, String tableName, String targetField, String amountExpression,
String[] conditionFields)
{
String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid");
String safeTargetField = requireSafeIdentifier(targetField, "Business effect target field is invalid");
BigDecimal amount = resolveBusinessNumber(params, amountExpression);
String whereClause = buildWhereClause(params, conditionFields);
List<Object> args = new ArrayList<Object>();
args.add(amount);
appendConditionValues(args, params, conditionFields);
return jdbcTemplate.update("update " + safeTableName + " set " + safeTargetField + " = " + safeTargetField + " + ? where " + whereClause, args.toArray());
}
private int decreaseNumberField(Map<String, Object> params, String tableName, String targetField, String amountExpression,
String minValueExpression, String[] conditionFields, String message)
{
String safeTableName = requireSafeIdentifier(tableName, "Business effect target table is invalid");
String safeTargetField = requireSafeIdentifier(targetField, "Business effect target field is invalid");
BigDecimal amount = resolveBusinessNumber(params, amountExpression);
BigDecimal minValue = resolveBusinessNumber(params, minValueExpression == null || minValueExpression.length() == 0 ? "0" : minValueExpression);
String whereClause = buildWhereClause(params, conditionFields);
List<Object> args = new ArrayList<Object>();
args.add(amount);
appendConditionValues(args, params, conditionFields);
args.add(amount);
args.add(minValue);
int rows = jdbcTemplate.update("update " + safeTableName + " set " + safeTargetField + " = " + safeTargetField + " - ? where " + whereClause + " and " + safeTargetField + " - ? >= ?", args.toArray());
if (rows == 0) {
throw new ServiceException(defaultMessage(message, "Business numeric decrease failed"));
}
return rows;
}
private int executeUpdateFieldEffect(Map<String, Object> params, String tableName, String targetField,
String valueExpression, String[] conditionFields)
{
@@ -438,6 +620,68 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
return values;
}
private int countRows(Map<String, Object> params, String tableName, String[] conditionFields)
{
String safeTableName = requireSafeIdentifier(tableName, "Business rule target table is invalid");
String whereClause = buildWhereClause(params, conditionFields);
Integer count = jdbcTemplate.queryForObject("select count(1) from " + safeTableName + " where " + whereClause, conditionValues(params, conditionFields), Integer.class);
return count == null ? 0 : count.intValue();
}
private Object querySingleField(Map<String, Object> params, String tableName, String targetField, String[] conditionFields)
{
String safeTableName = requireSafeIdentifier(tableName, "Business rule target table is invalid");
String safeTargetField = requireSafeIdentifier(targetField, "Business rule target field is invalid");
String whereClause = buildWhereClause(params, conditionFields);
List<Map<String, Object>> rows = jdbcTemplate.queryForList("select " + safeTargetField + " from " + safeTableName + " where " + whereClause + " limit 1", conditionValues(params, conditionFields));
if (rows.isEmpty()) {
return null;
}
return rows.get(0).get(safeTargetField);
}
private void appendConditionValues(List<Object> args, Map<String, Object> params, String[] conditionFields)
{
for (Object value : conditionValues(params, conditionFields)) {
args.add(value);
}
}
private Object resolveBusinessValue(Map<String, Object> params, String valueExpression)
{
if (valueExpression == null) {
return null;
}
if ("#[[${now}]]#".equals(valueExpression)) {
return new Date();
}
if (valueExpression.startsWith("#[[${param.]]#") && valueExpression.endsWith("}")) {
String field = valueExpression.substring(8, valueExpression.length() - 1);
requireBusinessParam(params, field);
return params.get(field);
}
return valueExpression;
}
private BigDecimal resolveBusinessNumber(Map<String, Object> params, String valueExpression)
{
return toBigDecimal(resolveBusinessValue(params, valueExpression), "Business numeric expression is invalid");
}
private BigDecimal toBigDecimal(Object value, String message)
{
try {
return new BigDecimal(String.valueOf(value));
} catch (RuntimeException e) {
throw new ServiceException(message);
}
}
private String defaultMessage(String message, String fallback)
{
return message == null || message.length() == 0 ? fallback : message;
}
private String requireSafeIdentifier(String value, String message)
{
if (value == null || !value.matches("[A-Za-z][A-Za-z0-9_]*")) {
@@ -793,7 +1037,7 @@ export default {
submitForm() {
const request = this.form.${pkColumn.javaField} ? ${className}Api.update${ClassName}(this.form) : ${className}Api.add${ClassName}(this.form)
request.then(() => {
this.$modal.msgSuccess("保存成功")
this.#[[$modal]]#.msgSuccess("保存成功")
this.open = false
this.getList()
})
@@ -801,14 +1045,14 @@ export default {
handleDelete(row) {
const id = row.${pkColumn.javaField}
${className}Api.del${ClassName}(id).then(() => {
this.$modal.msgSuccess("删除成功")
this.#[[$modal]]#.msgSuccess("删除成功")
this.getList()
})
},
handleBusinessAction(row, actionCode) {
const action = this.businessActions.find(item => item.code === actionCode)
${className}Api[actionCode]({ ...row }).then(() => {
this.$modal.msgSuccess((action ? action.name : "业务动作") + "成功")
this.#[[$modal]]#.msgSuccess((action ? action.name : "业务动作") + "成功")
this.getList()
})
},

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
-- =================================================
-- EasyCode front_project incremental upgrade
-- Run once on existing front workbench databases created before the current
-- front_project schema. Safe to run repeatedly.
-- =================================================
set @front_project_requirement_keyword_exists := (
select count(1)
from information_schema.columns
where table_schema = database()
and table_name = 'front_project'
and column_name = 'requirement_keyword'
);
set @front_project_requirement_keyword_sql := if(
@front_project_requirement_keyword_exists = 0,
'alter table front_project add column requirement_keyword varchar(1000) default '''' comment ''需求关键词'' after project_desc',
'select ''front_project.requirement_keyword already exists'''
);
prepare front_project_requirement_keyword_stmt from @front_project_requirement_keyword_sql;
execute front_project_requirement_keyword_stmt;
deallocate prepare front_project_requirement_keyword_stmt;
set @front_project_industry_template_exists := (
select count(1)
from information_schema.columns
where table_schema = database()
and table_name = 'front_project'
and column_name = 'industry_template'
);
set @front_project_industry_template_sql := if(
@front_project_industry_template_exists = 0,
'alter table front_project add column industry_template varchar(50) default '''' comment ''行业模板'' after requirement_keyword',
'select ''front_project.industry_template already exists'''
);
prepare front_project_industry_template_stmt from @front_project_industry_template_sql;
execute front_project_industry_template_stmt;
deallocate prepare front_project_industry_template_stmt;
set @front_project_app_blueprint_exists := (
select count(1)
from information_schema.columns
where table_schema = database()
and table_name = 'front_project'
and column_name = 'app_blueprint'
);
set @front_project_app_blueprint_sql := if(
@front_project_app_blueprint_exists = 0,
'alter table front_project add column app_blueprint longtext comment ''应用蓝图草稿JSON'' after preview_status',
'select ''front_project.app_blueprint already exists'''
);
prepare front_project_app_blueprint_stmt from @front_project_app_blueprint_sql;
execute front_project_app_blueprint_stmt;
deallocate prepare front_project_app_blueprint_stmt;
set @front_project_er_diagram_exists := (
select count(1)
from information_schema.columns
where table_schema = database()
and table_name = 'front_project'
and column_name = 'er_diagram'
);
set @front_project_er_diagram_sql := if(
@front_project_er_diagram_exists = 0,
'alter table front_project add column er_diagram longtext comment ''ER图草稿JSON'' after app_blueprint',
'select ''front_project.er_diagram already exists'''
);
prepare front_project_er_diagram_stmt from @front_project_er_diagram_sql;
execute front_project_er_diagram_stmt;
deallocate prepare front_project_er_diagram_stmt;
set @front_project_business_blueprint_exists := (
select count(1)
from information_schema.columns
where table_schema = database()
and table_name = 'front_project'
and column_name = 'business_blueprint'
);
set @front_project_business_blueprint_sql := if(
@front_project_business_blueprint_exists = 0,
'alter table front_project add column business_blueprint longtext comment ''业务蓝图草稿JSON'' after er_diagram',
'select ''front_project.business_blueprint already exists'''
);
prepare front_project_business_blueprint_stmt from @front_project_business_blueprint_sql;
execute front_project_business_blueprint_stmt;
deallocate prepare front_project_business_blueprint_stmt;
set @front_project_frontend_enabled_exists := (
select count(1)
from information_schema.columns
where table_schema = database()
and table_name = 'front_project'
and column_name = 'frontend_enabled'
);
set @front_project_frontend_enabled_sql := if(
@front_project_frontend_enabled_exists = 0,
'alter table front_project add column frontend_enabled char(1) default ''1'' comment ''是否生成前台前端1是 0否'' after back_framework',
'select ''front_project.frontend_enabled already exists'''
);
prepare front_project_frontend_enabled_stmt from @front_project_frontend_enabled_sql;
execute front_project_frontend_enabled_stmt;
deallocate prepare front_project_frontend_enabled_stmt;
create table if not exists front_project_module (
id bigint(20) not null auto_increment comment 'id',
project_id bigint(20) not null comment '前台项目ID',
module_id bigint(20) not null comment '功能模块ID',
status char(1) default '0' comment '状态0正常 1停用',
create_time datetime default null comment '创建时间',
update_time datetime default null comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (id),
unique key uk_front_project_module (project_id, module_id),
key idx_front_project_module_project_id (project_id),
key idx_front_project_module_module_id (module_id),
constraint fk_front_project_module_project foreign key (project_id) references front_project(project_id),
constraint fk_front_project_module_module foreign key (module_id) references sys_module(module_id)
) engine=innodb auto_increment=100 default charset=utf8mb4 comment='前台项目功能模块关系';

View File

@@ -7,6 +7,7 @@ drop table if exists front_project_file;
drop table if exists front_project_generation;
drop table if exists front_project_column;
drop table if exists front_project_table;
drop table if exists front_project_module;
drop table if exists front_project;
drop table if exists front_user;
@@ -41,6 +42,7 @@ create table front_project (
industry_template varchar(50) default '' comment '行业模板',
front_framework varchar(50) default 'Vue3 + Element Plus' comment '前台框架',
back_framework varchar(50) default 'Spring Boot + MyBatis' comment '后端框架',
frontend_enabled char(1) default '1' comment '是否生成前台前端1是 0否',
generate_status char(1) default '0' comment '数据库生成状态0未生成 1生成中 2已生成 3失败',
preview_status char(1) default '0' comment '预览状态0未预览 1已预览 2失败',
app_blueprint longtext comment '应用蓝图草稿JSON',
@@ -55,6 +57,22 @@ create table front_project (
constraint fk_front_project_user foreign key (user_id) references front_user(user_id)
) engine=innodb auto_increment=100 default charset=utf8mb4 comment='前台项目草稿表';
create table front_project_module (
id bigint(20) not null auto_increment comment 'id',
project_id bigint(20) not null comment '前台项目ID',
module_id bigint(20) not null comment '功能模块ID',
status char(1) default '0' comment '状态0正常 1停用',
create_time datetime default null comment '创建时间',
update_time datetime default null comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (id),
unique key uk_front_project_module (project_id, module_id),
key idx_front_project_module_project_id (project_id),
key idx_front_project_module_module_id (module_id),
constraint fk_front_project_module_project foreign key (project_id) references front_project(project_id),
constraint fk_front_project_module_module foreign key (module_id) references sys_module(module_id)
) engine=innodb auto_increment=100 default charset=utf8mb4 comment='前台项目功能模块关系';
create table front_project_table (
table_id bigint(20) not null auto_increment comment '表设计ID',
project_id bigint(20) not null comment '项目ID',