Files
yidaima/RuoYi-Vue/docs/superpowers/plans/2026-05-21-easycode-front-workbench.md

56 KiB
Raw Blame History

EasyCode Front Workbench Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build an independent EasyCode front workbench where front users log in, generate editable MySQL table drafts through a backend DeepSeek service, preview backend/frontend/admin project files, and download generated source.

Architecture: Add a standalone easycode-web Vue 3 + Vite app for public/front-user flows. Add /front/** APIs to ruoyi-admin, store front-user drafts in new front_* tables under ruoyi-generator, call DeepSeek only through backend AiGenerateService, and reuse the existing generator rendering logic for preview/download.

Tech Stack: Spring Boot 2.5, Spring Security, MyBatis XML, Fastjson2, Velocity generator, Vue 3, Vite, Vue Router, Axios, Element Plus, Sass.


Scope Check

This is a cross-cutting feature, but the pieces are sequential parts of one product workflow:

  1. Database/auth foundation.
  2. Project draft and editable database design.
  3. DeepSeek-backed database generation.
  4. Generator preview/download adaptation.
  5. Independent front app and end-to-end integration.

Implement in the task order below. Each task should end with tests and a commit.

File Map

SQL

  • Create sql/front_workbench.sql: standalone DDL for new front-user and draft tables.
  • Modify sql/db.sql: include the same DDL so a fresh database has the front workbench tables.

Common and Framework

  • Modify ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java: add login type constants.
  • Modify ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java: add loginType.
  • Modify ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java: prevent front tokens from authenticating non-/front/** requests, and prevent admin tokens from authenticating protected /front/** requests.
  • Modify ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java: allow anonymous /front/auth/login and /front/auth/register.
  • Modify ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java: mark admin logins as admin tokens.

Generator Module

  • Create domain classes under ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/:
    • FrontUser.java
    • FrontProject.java
    • FrontProjectTable.java
    • FrontProjectColumn.java
    • FrontProjectGeneration.java
    • FrontProjectFile.java
  • Create DTO classes under ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/:
    • FrontLoginBody.java
    • FrontRegisterBody.java
    • FrontProjectCreateRequest.java
    • FrontProjectUpdateRequest.java
    • GenerateDatabaseRequest.java
    • DatabaseDesignResponse.java
    • DatabaseTableDesign.java
    • DatabaseColumnDesign.java
    • PreviewFileRequest.java
    • ProjectStructureResponse.java
  • Create mappers under ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/front/:
    • FrontUserMapper.java
    • FrontProjectMapper.java
    • FrontProjectTableMapper.java
    • FrontProjectColumnMapper.java
    • FrontProjectGenerationMapper.java
    • FrontProjectFileMapper.java
  • Create mapper XML under ruoyi-generator/src/main/resources/mapper/front/ with matching names.
  • Create services under ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/:
    • IFrontUserService.java
    • FrontUserServiceImpl.java
    • IFrontProjectService.java
    • FrontProjectServiceImpl.java
    • IAiGenerateService.java
    • AiGenerateServiceImpl.java
    • IDeepSeekClient.java
    • HttpDeepSeekClient.java
    • AiDatabaseSchemaValidator.java
    • MysqlDdlBuilder.java
    • FrontProjectConverter.java
    • IFrontProjectPreviewService.java
    • FrontProjectPreviewServiceImpl.java
  • Modify ruoyi-generator/src/main/java/com/ruoyi/generator/config/:
    • Create DeepSeekProperties.java.
  • Modify existing generator rendering:
    • ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenProjectService.java
    • ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java Add overloads that accept a transient GenProject with draft tables.

Admin Controllers

  • Create ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontAuthController.java.
  • Create ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java.
  • Create ruoyi-admin/src/main/java/com/ruoyi/web/service/front/FrontAuthService.java.

Front App

  • Create project root easycode-web/.
  • Create easycode-web/package.json, vite.config.js, index.html, .env.development, .env.production.
  • Create app files:
    • easycode-web/src/main.js
    • easycode-web/src/App.vue
    • easycode-web/src/router/index.js
    • easycode-web/src/api/request.js
    • easycode-web/src/api/auth.js
    • easycode-web/src/api/project.js
    • easycode-web/src/utils/auth.js
    • easycode-web/src/styles/global.scss
    • easycode-web/src/components/AppHeader.vue
    • easycode-web/src/components/DatabaseDesigner.vue
    • easycode-web/src/components/ProjectStructureTree.vue
    • easycode-web/src/components/CodePreview.vue
    • easycode-web/src/views/HomeView.vue
    • easycode-web/src/views/LoginView.vue
    • easycode-web/src/views/RegisterView.vue
    • easycode-web/src/views/GenerateView.vue
    • easycode-web/src/views/PreviewView.vue

Task 1: Add Front Workbench Database Tables

Files:

  • Create: sql/front_workbench.sql

  • Modify: sql/db.sql

  • Step 1: Write the DDL script

Create sql/front_workbench.sql with this schema. Use utf8mb4, innodb, and front_ prefixes.

-- =================================================
-- EasyCode front workbench tables
-- Import after sql/db.sql, or use the copy appended to sql/db.sql
-- =================================================

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;
drop table if exists front_user;

create table front_user (
  user_id              bigint(20)      not null auto_increment comment '前台用户ID',
  username             varchar(50)     not null comment '用户名',
  password             varchar(100)    not null comment '密码',
  nickname             varchar(50)     default '' comment '昵称',
  email                varchar(100)    default '' comment '邮箱',
  phone                varchar(20)     default '' comment '手机号',
  status               char(1)         default '0' comment '状态0正常 1停用',
  del_flag             char(1)         default '0' comment '删除标志0存在 2删除',
  last_login_time      datetime        default null comment '最后登录时间',
  create_time          datetime        default null comment '创建时间',
  update_time          datetime        default null comment '更新时间',
  remark               varchar(500)    default null comment '备注',
  primary key (user_id),
  unique key uk_front_user_username (username),
  key idx_front_user_email (email),
  key idx_front_user_phone (phone)
) engine=innodb auto_increment=100 comment='前台用户表';

create table front_project (
  project_id           bigint(20)      not null auto_increment comment '前台项目ID',
  user_id              bigint(20)      not null comment '前台用户ID',
  project_name         varchar(100)    not null comment '项目名称',
  project_file_name    varchar(100)    default '' comment '项目文件名',
  package_name         varchar(100)    default 'com.easycode.generated' comment '包名',
  version              varchar(20)     default '1.0.0' comment '版本号',
  project_desc         varchar(1000)   default '' comment '项目描述',
  industry_template    varchar(50)     default '' comment '行业模板',
  front_framework      varchar(50)     default 'Vue3 + Element Plus' comment '前台框架',
  back_framework       varchar(50)     default 'Spring Boot + MyBatis' comment '后端框架',
  generate_status      char(1)         default '0' comment '数据库生成状态0未生成 1生成中 2已生成 3失败',
  preview_status       char(1)         default '0' comment '预览状态0未预览 1已预览 2失败',
  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 (project_id),
  key idx_front_project_user_id (user_id),
  constraint fk_front_project_user foreign key (user_id) references front_user(user_id)
) engine=innodb auto_increment=100 comment='前台项目草稿表';

create table front_project_table (
  table_id             bigint(20)      not null auto_increment comment '表设计ID',
  project_id           bigint(20)      not null comment '项目ID',
  user_id              bigint(20)      not null comment '前台用户ID',
  table_name           varchar(200)    not null comment '表名称',
  table_comment        varchar(500)    default '' comment '表描述',
  class_name           varchar(100)    default '' comment '实体类名称',
  module_name          varchar(30)     default '' comment '生成模块名',
  business_name        varchar(30)     default '' comment '生成业务名',
  function_name        varchar(50)     default '' comment '生成功能名',
  tpl_category         varchar(200)    default 'crud' comment '模板类型',
  tpl_web_type         varchar(30)     default 'element-plus' comment '前端模板类型',
  options              varchar(1000)   default '' comment '生成选项',
  create_table_sql     longtext        comment '建表SQL',
  sort                 int             default 0 comment '排序',
  status               char(1)         default '0' comment '状态0正常 1停用',
  create_time          datetime        default null comment '创建时间',
  update_time          datetime        default null comment '更新时间',
  primary key (table_id),
  unique key uk_front_project_table (project_id, table_name),
  key idx_front_project_table_project_id (project_id),
  constraint fk_front_project_table_project foreign key (project_id) references front_project(project_id)
) engine=innodb auto_increment=100 comment='前台项目表设计';

create table front_project_column (
  column_id            bigint(20)      not null auto_increment comment '字段设计ID',
  table_id             bigint(20)      not null comment '表设计ID',
  project_id           bigint(20)      not null comment '项目ID',
  column_name          varchar(200)    not null comment '字段名称',
  column_comment       varchar(500)    default '' comment '字段描述',
  column_type          varchar(100)    not null comment '字段类型',
  java_type            varchar(500)    default 'String' comment 'Java类型',
  java_field           varchar(200)    default '' comment 'Java字段名',
  is_pk                char(1)         default '0' comment '是否主键1是',
  is_increment         char(1)         default '0' comment '是否自增1是',
  is_required          char(1)         default '0' comment '是否必填1是',
  is_insert            char(1)         default '1' comment '是否插入字段1是',
  is_edit              char(1)         default '1' comment '是否编辑字段1是',
  is_list              char(1)         default '1' comment '是否列表字段1是',
  is_query             char(1)         default '0' comment '是否查询字段1是',
  query_type           varchar(200)    default 'EQ' comment '查询方式',
  html_type            varchar(200)    default 'input' comment '显示类型',
  dict_type            varchar(200)    default '' comment '字典类型',
  default_value        varchar(200)    default null comment '默认值',
  sort                 int             default 0 comment '排序',
  create_time          datetime        default null comment '创建时间',
  update_time          datetime        default null comment '更新时间',
  primary key (column_id),
  key idx_front_project_column_table_id (table_id),
  key idx_front_project_column_project_id (project_id),
  constraint fk_front_project_column_table foreign key (table_id) references front_project_table(table_id)
) engine=innodb auto_increment=100 comment='前台项目字段设计';

create table front_project_generation (
  generation_id        bigint(20)      not null auto_increment comment '生成记录ID',
  project_id           bigint(20)      not null comment '项目ID',
  user_id              bigint(20)      not null comment '前台用户ID',
  generate_type        varchar(50)     not null comment '生成类型',
  provider             varchar(50)     default 'deepseek' comment 'AI提供方',
  prompt_summary       varchar(500)    default '' comment 'Prompt摘要',
  request_payload      longtext        comment '请求内容',
  response_payload     longtext        comment '响应内容',
  success              char(1)         default '0' comment '是否成功1成功 0失败',
  error_message        varchar(1000)   default '' comment '错误信息',
  token_usage          int             default 0 comment 'Token用量',
  elapsed_ms           bigint(20)      default 0 comment '耗时毫秒',
  create_time          datetime        default null comment '创建时间',
  primary key (generation_id),
  key idx_front_project_generation_project_id (project_id)
) engine=innodb auto_increment=100 comment='前台项目生成记录';

create table front_project_file (
  file_id              bigint(20)      not null auto_increment comment '文件缓存ID',
  project_id           bigint(20)      not null comment '项目ID',
  template_type        varchar(50)     not null comment '模板类型',
  node_id              varchar(100)    default '' comment '结构节点ID',
  table_id             bigint(20)      default null comment '表设计ID',
  file_name            varchar(255)    not null comment '文件名',
  file_path            varchar(500)    default '' comment '文件路径',
  file_content         longtext        comment '文件内容',
  content_hash         varchar(64)     default '' comment '内容哈希',
  create_time          datetime        default null comment '创建时间',
  update_time          datetime        default null comment '更新时间',
  primary key (file_id),
  key idx_front_project_file_project_id (project_id),
  key idx_front_project_file_lookup (project_id, template_type, table_id)
) engine=innodb auto_increment=100 comment='前台项目预览文件缓存';
  • Step 2: Append the same DDL to sql/db.sql

Place it after the existing generator extension tables and before SET FOREIGN_KEY_CHECKS = 1;. Keep drop table if exists order from child tables to parent tables.

  • Step 3: Run SQL static checks

Run:

git diff --check -- sql/db.sql sql/front_workbench.sql
rg -n "create table front_user|create table front_project|front_project_generation" sql/db.sql sql/front_workbench.sql

Expected:

git diff --check has no output
rg prints all six new front_* table declarations
  • Step 4: Commit
git add sql/db.sql sql/front_workbench.sql
git commit -m "Add front workbench database schema"

Task 2: Add Front Token Isolation

Files:

  • Modify: ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

  • Modify: ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java

  • Modify: ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java

  • Modify: ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java

  • Modify: ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

  • Step 1: Add login type constants

In Constants.java, add:

/** 后台管理端登录类型 */
public static final String LOGIN_TYPE_ADMIN = "admin";

/** 前台用户登录类型 */
public static final String LOGIN_TYPE_FRONT = "front";
  • Step 2: Add loginType to LoginUser

Add field, getter, and setter:

/** 登录类型admin 后台用户front 前台用户 */
private String loginType;

public String getLoginType()
{
    return loginType;
}

public void setLoginType(String loginType)
{
    this.loginType = loginType;
}

Keep existing constructors unchanged. Existing tokens with loginType == null must be treated as admin-compatible.

  • Step 3: Mark existing admin logins

In SysLoginService.login, immediately after:

LoginUser loginUser = (LoginUser) authentication.getPrincipal();

add:

loginUser.setLoginType(Constants.LOGIN_TYPE_ADMIN);
  • Step 4: Gate front/admin tokens in JWT filter

In JwtAuthenticationTokenFilter, add helper methods:

private boolean isFrontRequest(HttpServletRequest request)
{
    return request.getRequestURI() != null && request.getRequestURI().startsWith("/front/");
}

private boolean isTokenAllowedForRequest(LoginUser loginUser, HttpServletRequest request)
{
    String loginType = loginUser.getLoginType();
    boolean frontRequest = isFrontRequest(request);
    if (frontRequest)
    {
        return com.ruoyi.common.constant.Constants.LOGIN_TYPE_FRONT.equals(loginType);
    }
    return loginType == null || com.ruoyi.common.constant.Constants.LOGIN_TYPE_ADMIN.equals(loginType);
}

Then change the existing authentication block to:

if (StringUtils.isNotNull(loginUser)
        && isTokenAllowedForRequest(loginUser, request)
        && StringUtils.isNull(SecurityUtils.getAuthentication()))
{
    tokenService.verifyToken(loginUser);
    UsernamePasswordAuthenticationToken authenticationToken =
            new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
  • Step 5: Permit front login/register anonymously

In SecurityConfig.filterChain, extend the anonymous matcher:

requests.antMatchers("/login", "/register", "/captchaImage",
        "/front/auth/login", "/front/auth/register").permitAll()

Do not permit all /front/**.

  • Step 6: Run backend compile

Run:

mvn -pl ruoyi-admin -am -DskipTests package

Expected:

[INFO] BUILD SUCCESS
  • Step 7: Commit
git add ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java `
  ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java `
  ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java `
  ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java `
  ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
git commit -m "Separate front and admin login tokens"

Task 3: Add Front Domain, Mappers, and Basic Services

Files:

  • Create: front domain classes, mapper interfaces, mapper XML, and basic service interfaces/impls listed in File Map.

  • Test: ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/FrontProjectConverterTest.java

  • Step 1: Create front domain classes

Each domain class extends BaseEntity where timestamps and remark are needed. Use JavaBean getters/setters, not Lombok.

FrontUser fields:

private Long userId;
private String username;
private String password;
private String nickname;
private String email;
private String phone;
private String status;
private String delFlag;
private Date lastLoginTime;

FrontProject fields:

private Long projectId;
private Long userId;
private String projectName;
private String projectFileName;
private String packageName;
private String version;
private String projectDesc;
private String industryTemplate;
private String frontFramework;
private String backFramework;
private String generateStatus;
private String previewStatus;
private String status;
private List<FrontProjectTable> tables;

FrontProjectTable fields:

private Long tableId;
private Long projectId;
private Long userId;
private String tableName;
private String tableComment;
private String className;
private String moduleName;
private String businessName;
private String functionName;
private String tplCategory;
private String tplWebType;
private String options;
private String createTableSql;
private Integer sort;
private String status;
private List<FrontProjectColumn> columns;

FrontProjectColumn fields:

private Long columnId;
private Long tableId;
private Long projectId;
private String columnName;
private String columnComment;
private String columnType;
private String javaType;
private String javaField;
private String isPk;
private String isIncrement;
private String isRequired;
private String isInsert;
private String isEdit;
private String isList;
private String isQuery;
private String queryType;
private String htmlType;
private String dictType;
private String defaultValue;
private Integer sort;

FrontProjectGeneration and FrontProjectFile mirror the SQL columns.

  • Step 2: Create mapper interfaces

Use this shape for every mapper:

public interface FrontProjectMapper
{
    FrontProject selectFrontProjectById(Long projectId);

    List<FrontProject> selectFrontProjectList(FrontProject frontProject);

    int insertFrontProject(FrontProject frontProject);

    int updateFrontProject(FrontProject frontProject);

    int deleteFrontProjectById(Long projectId);
}

Add ownership-specific methods where needed:

FrontProject selectFrontProjectByUserAndId(@Param("userId") Long userId, @Param("projectId") Long projectId);
int deleteFrontProjectByUserAndId(@Param("userId") Long userId, @Param("projectId") Long projectId);

For table and column mappers add:

List<FrontProjectTable> selectTablesByProjectId(Long projectId);
int deleteTablesByProjectId(Long projectId);
List<FrontProjectColumn> selectColumnsByTableId(Long tableId);
int deleteColumnsByProjectId(Long projectId);
  • Step 3: Create mapper XML

Follow the existing XML style in mapper/generator/*.xml. Every XML must contain:

<resultMap type="FrontProject" id="FrontProjectResult">
    <id property="projectId" column="project_id" />
    <result property="userId" column="user_id" />
    <result property="projectName" column="project_name" />
    <result property="projectFileName" column="project_file_name" />
    <result property="packageName" column="package_name" />
    <result property="version" column="version" />
    <result property="projectDesc" column="project_desc" />
    <result property="industryTemplate" column="industry_template" />
    <result property="frontFramework" column="front_framework" />
    <result property="backFramework" column="back_framework" />
    <result property="generateStatus" column="generate_status" />
    <result property="previewStatus" column="preview_status" />
    <result property="status" column="status" />
    <result property="createTime" column="create_time" />
    <result property="updateTime" column="update_time" />
    <result property="remark" column="remark" />
</resultMap>

Use useGeneratedKeys="true" and keyProperty for inserts.

  • Step 4: Create basic services

IFrontProjectService must expose:

FrontProject createProject(Long userId, FrontProjectCreateRequest request);
List<FrontProject> listProjects(Long userId);
FrontProject getProject(Long userId, Long projectId);
int updateProject(Long userId, Long projectId, FrontProjectUpdateRequest request);
int deleteProject(Long userId, Long projectId);
DatabaseDesignResponse getDatabase(Long userId, Long projectId);
DatabaseDesignResponse saveDatabase(Long userId, Long projectId, DatabaseDesignResponse request);

saveDatabase must delete old front_project_column, then old front_project_table, then insert the submitted table/column tree in one transaction.

  • Step 5: Write converter test first

Create FrontProjectConverterTest with this test:

@Test
public void convertsFrontDraftToGenProject()
{
    FrontProject project = new FrontProject();
    project.setProjectId(10L);
    project.setProjectName("车辆管理系统");
    project.setProjectFileName("vehicle-system");
    project.setPackageName("com.easycode.vehicle");
    project.setVersion("1.0.0");
    project.setFrontFramework("Vue3 + Element Plus");
    project.setBackFramework("Spring Boot + MyBatis");

    FrontProjectTable table = new FrontProjectTable();
    table.setTableId(20L);
    table.setTableName("car_info");
    table.setTableComment("车辆信息表");
    table.setClassName("CarInfo");
    table.setModuleName("vehicle");
    table.setBusinessName("car");
    table.setFunctionName("车辆信息");

    FrontProjectColumn column = new FrontProjectColumn();
    column.setColumnId(30L);
    column.setColumnName("car_id");
    column.setColumnComment("车辆ID");
    column.setColumnType("bigint(20)");
    column.setJavaType("Long");
    column.setJavaField("carId");
    column.setIsPk("1");
    column.setIsIncrement("1");
    table.setColumns(Collections.singletonList(column));
    project.setTables(Collections.singletonList(table));

    GenProject genProject = FrontProjectConverter.toGenProject(project);

    assertEquals("车辆管理系统", genProject.getProjectName());
    assertEquals("com.easycode.vehicle", genProject.getPackageName());
    assertEquals(1, genProject.getTables().size());
    assertEquals("car_info", genProject.getTables().get(0).getTableName());
    assertEquals("carId", genProject.getTables().get(0).getColumns().get(0).getJavaField());
}
  • Step 6: Run test and verify failure

Run:

mvn -pl ruoyi-generator -am -Dtest=FrontProjectConverterTest test

Expected before converter implementation:

Compilation failure: cannot find symbol FrontProjectConverter
  • Step 7: Implement FrontProjectConverter

Implement exact conversion rules:

  • FrontProject.projectId maps to GenProject.projectId.

  • FrontProject.projectName maps to GenProject.projectName.

  • FrontProject.projectFileName maps to GenProject.projectFileName.

  • FrontProject.packageName maps to GenProject.packageName.

  • FrontProject.version maps to GenProject.version.

  • FrontProject.frontFramework maps to GenProject.frontFramework.

  • FrontProject.backFramework maps to GenProject.backFramework.

  • Each FrontProjectTable maps to GenTable.

  • Each FrontProjectColumn maps to GenTableColumn.

  • Step 8: Run tests

Run:

mvn -pl ruoyi-generator -am -Dtest=FrontProjectConverterTest test

Expected:

Tests run: 1, Failures: 0, Errors: 0
  • Step 9: Commit
git add ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front `
  ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/front `
  ruoyi-generator/src/main/resources/mapper/front `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front `
  ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/FrontProjectConverterTest.java
git commit -m "Add front project draft model"

Task 4: Add Front Authentication APIs

Files:

  • Create: ruoyi-admin/src/main/java/com/ruoyi/web/service/front/FrontAuthService.java

  • Create: ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontAuthController.java

  • Create DTOs if not already created:

    • FrontLoginBody.java
    • FrontRegisterBody.java
  • Modify: IFrontUserService.java, FrontUserServiceImpl.java

  • Step 1: Add front user service methods

IFrontUserService must include:

FrontUser selectByUsername(String username);
FrontUser selectById(Long userId);
int register(FrontRegisterBody body);
int updateLastLoginTime(Long userId);

Registration rules:

  • username required, length 2 to 50

  • password required, length 5 to 50

  • duplicate username raises ServiceException("前台账号已存在")

  • password stored with SecurityUtils.encryptPassword

  • default nickname is username

  • default status is "0"

  • default delFlag is "0"

  • Step 2: Implement front auth service

FrontAuthService.login must:

  1. Load front user by username.
  2. Reject missing user, disabled user, deleted user, and wrong password.
  3. Build a SysUser object only for token compatibility.
  4. Build a LoginUser.
  5. Set loginType to Constants.LOGIN_TYPE_FRONT.
  6. Set permissions to Collections.emptySet().
  7. Call tokenService.createToken(loginUser).
  8. Update last_login_time.

Core snippet:

SysUser tokenUser = new SysUser();
tokenUser.setUserId(frontUser.getUserId());
tokenUser.setUserName(frontUser.getUsername());
tokenUser.setNickName(frontUser.getNickname());
tokenUser.setPassword(frontUser.getPassword());

LoginUser loginUser = new LoginUser(frontUser.getUserId(), null, tokenUser, Collections.emptySet());
loginUser.setLoginType(Constants.LOGIN_TYPE_FRONT);
String token = tokenService.createToken(loginUser);
frontUserService.updateLastLoginTime(frontUser.getUserId());
return token;
  • Step 3: Implement controller

FrontAuthController endpoints:

@RestController
@RequestMapping("/front/auth")
public class FrontAuthController extends BaseController
{
    @PostMapping("/register")
    public AjaxResult register(@RequestBody FrontRegisterBody body)

    @PostMapping("/login")
    public AjaxResult login(@RequestBody FrontLoginBody body)

    @GetMapping("/profile")
    public AjaxResult profile()

    @PostMapping("/logout")
    public AjaxResult logout()
}

login returns:

AjaxResult ajax = AjaxResult.success();
ajax.put(Constants.TOKEN, token);
return ajax;

profile returns the front user from FrontUserService.selectById(SecurityUtils.getUserId()).

  • Step 4: Add service test

Create ruoyi-admin/src/test/java/com/ruoyi/web/service/front/FrontAuthServiceTest.java. Add test dependencies to ruoyi-admin/pom.xml if missing:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <scope>test</scope>
</dependency>

Test:

@Test
public void loginCreatesFrontToken()
{
    FrontUser user = new FrontUser();
    user.setUserId(100L);
    user.setUsername("demo");
    user.setNickname("Demo");
    user.setPassword(SecurityUtils.encryptPassword("admin123"));
    user.setStatus("0");
    user.setDelFlag("0");

    when(frontUserService.selectByUsername("demo")).thenReturn(user);
    when(tokenService.createToken(any(LoginUser.class))).thenAnswer(invocation -> {
        LoginUser loginUser = invocation.getArgument(0);
        assertEquals(Constants.LOGIN_TYPE_FRONT, loginUser.getLoginType());
        assertEquals(Long.valueOf(100L), loginUser.getUserId());
        return "front-token";
    });

    String token = frontAuthService.login("demo", "admin123");

    assertEquals("front-token", token);
    verify(frontUserService).updateLastLoginTime(100L);
}
  • Step 5: Run tests

Run:

mvn -pl ruoyi-admin -am -Dtest=FrontAuthServiceTest test

Expected:

Tests run: 1, Failures: 0, Errors: 0
  • Step 6: Commit
git add ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontAuthController.java `
  ruoyi-admin/src/main/java/com/ruoyi/web/service/front/FrontAuthService.java `
  ruoyi-admin/src/test/java/com/ruoyi/web/service/front/FrontAuthServiceTest.java `
  ruoyi-admin/pom.xml `
  ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/FrontLoginBody.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto/FrontRegisterBody.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IFrontUserService.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/FrontUserServiceImpl.java
git commit -m "Add front user authentication"

Task 5: Add Project Draft APIs and Editable Database Save

Files:

  • Create: ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java

  • Modify: IFrontProjectService.java

  • Modify: FrontProjectServiceImpl.java

  • Create/modify DTO classes for project create/update/database response.

  • Step 1: Implement project CRUD controller

Controller methods:

@RestController
@RequestMapping("/front/project")
public class FrontProjectController extends BaseController
{
    @Autowired
    private IFrontProjectService frontProjectService;

    @PostMapping("/create")
    public AjaxResult create(@RequestBody FrontProjectCreateRequest request)

    @GetMapping("/list")
    public AjaxResult list()

    @GetMapping("/{projectId}")
    public AjaxResult get(@PathVariable Long projectId)

    @PutMapping("/{projectId}")
    public AjaxResult update(@PathVariable Long projectId, @RequestBody FrontProjectUpdateRequest request)

    @DeleteMapping("/{projectId}")
    public AjaxResult delete(@PathVariable Long projectId)

    @GetMapping("/{projectId}/database")
    public AjaxResult database(@PathVariable Long projectId)

    @PutMapping("/{projectId}/database")
    public AjaxResult saveDatabase(@PathVariable Long projectId, @RequestBody DatabaseDesignResponse request)
}

Every method gets Long userId = SecurityUtils.getUserId() and only touches rows owned by that userId.

  • Step 2: Implement project create defaults

FrontProjectServiceImpl.createProject must default:

projectFileName = lower hyphen form of projectName, e.g. "车辆管理系统" -> "easycode-project"
packageName = request.packageName if present else "com.easycode.generated"
version = request.version if present else "1.0.0"
frontFramework = "Vue3 + Element Plus"
backFramework = "Spring Boot + MyBatis"
generateStatus = "0"
previewStatus = "0"
status = "0"

When Chinese project names cannot be slugified safely, use "easycode-project-" + System.currentTimeMillis().

  • Step 3: Implement getDatabase

Return:

DatabaseDesignResponse response = new DatabaseDesignResponse();
response.setProjectId(projectId);
response.setTables(tableDesigns);
response.setSql(joinedCreateTableSql);

Join SQL with two line breaks between tables.

  • Step 4: Implement saveDatabase validation

Before saving:

  • project must belong to user
  • at least one table
  • every table name matches ^[a-z][a-z0-9_]{1,63}$
  • every table has at least one column
  • every column name matches ^[a-z][a-z0-9_]{1,63}$
  • every column type passes AiDatabaseSchemaValidator.isAllowedColumnType
  • exactly one primary key per table

Raise ServiceException with messages:

项目不存在或无权限
至少需要一张数据表
表名只能包含小写字母、数字和下划线
每张表至少需要一个字段
字段名只能包含小写字母、数字和下划线
字段类型不在允许范围内
每张表必须且只能有一个主键
  • Step 5: Run generator tests

Run:

mvn -pl ruoyi-generator -am -DfailIfNoTests=false test

Expected:

[INFO] BUILD SUCCESS
  • Step 6: Commit
git add ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/domain/front/dto `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IFrontProjectService.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/FrontProjectServiceImpl.java
git commit -m "Add front project draft APIs"

Task 6: Add DeepSeek-backed AI Database Generation

Files:

  • Create: ruoyi-generator/src/main/java/com/ruoyi/generator/config/DeepSeekProperties.java

  • Create/modify: IAiGenerateService.java, AiGenerateServiceImpl.java, IDeepSeekClient.java, HttpDeepSeekClient.java, AiDatabaseSchemaValidator.java, MysqlDdlBuilder.java

  • Modify: FrontProjectController.java

  • Modify: application.yml

  • Test: AiDatabaseSchemaValidatorTest.java, MysqlDdlBuilderTest.java, AiGenerateServiceImplTest.java

  • Step 1: Add DeepSeek config properties

Create:

@Component
@ConfigurationProperties(prefix = "deepseek")
public class DeepSeekProperties
{
    private String apiKey;
    private String baseUrl = "https://api.deepseek.com";
    private String model = "deepseek-chat";
    private Integer timeout = 60000;
    private Integer maxTables = 12;
    private Integer maxColumnsPerTable = 30;

    // getters and setters
}

Add to application.yml:

deepseek:
  apiKey: ${DEEPSEEK_API_KEY:}
  baseUrl: https://api.deepseek.com
  model: deepseek-chat
  timeout: 60000
  maxTables: 12
  maxColumnsPerTable: 30
  • Step 2: Implement DeepSeek client

IDeepSeekClient:

public interface IDeepSeekClient
{
    String chat(String prompt);
}

HttpDeepSeekClient uses RestTemplate. Request:

{
  "model": "deepseek-chat",
  "messages": [
    {"role": "system", "content": "你是企业管理系统数据库设计助手只返回JSON。"},
    {"role": "user", "content": "<prompt>"}
  ],
  "temperature": 0.2
}

Response parser returns choices[0].message.content. If apiKey is blank, throw:

throw new ServiceException("DeepSeek API Key 未配置");
  • Step 3: Implement validator

Allowed type patterns:

private static final Pattern NAME = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
private static final Pattern BIGINT = Pattern.compile("^bigint(\\(20\\))?$");
private static final Pattern INT = Pattern.compile("^int(\\(11\\))?$");
private static final Pattern VARCHAR = Pattern.compile("^varchar\\(([1-9][0-9]{0,2}|1000)\\)$");
private static final Pattern CHAR = Pattern.compile("^char\\(([1-9][0-9]?)\\)$");
private static final Pattern DECIMAL = Pattern.compile("^decimal\\(([1-9][0-9]?),([1-9][0-9]?)\\)$");

Also allow exact:

datetime, date, text, longtext

Reject SQL containing case-insensitive:

drop, truncate, alter user, grant, revoke, load_file, into outfile
  • Step 4: Implement DDL builder

For each table produce:

CREATE TABLE `car_info` (
  `car_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '车辆ID',
  `car_no` varchar(30) NOT NULL COMMENT '车牌号',
  PRIMARY KEY (`car_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆信息表';

Rules:

  • primary key with isIncrement == "1" gets AUTO_INCREMENT

  • required field gets NOT NULL

  • optional field gets DEFAULT NULL

  • string comments escape single quote as doubled single quote

  • one DDL string per table saved to FrontProjectTable.createTableSql

  • Step 5: Write validator tests

Test cases:

assertTrue(validator.isValidName("car_info"));
assertFalse(validator.isValidName("CarInfo"));
assertFalse(validator.isValidName("sys user"));
assertTrue(validator.isAllowedColumnType("varchar(100)"));
assertTrue(validator.isAllowedColumnType("decimal(10,2)"));
assertFalse(validator.isAllowedColumnType("json"));
assertFalse(validator.isSafeSql("drop table sys_user"));
  • Step 6: Write AI service test with mocked client

Mock response:

{
  "tables": [
    {
      "tableName": "car_info",
      "tableComment": "车辆信息表",
      "columns": [
        {
          "columnName": "car_id",
          "columnType": "bigint(20)",
          "javaType": "Long",
          "javaField": "carId",
          "isPk": "1",
          "isIncrement": "1",
          "isRequired": "1",
          "columnComment": "车辆ID"
        },
        {
          "columnName": "car_no",
          "columnType": "varchar(30)",
          "javaType": "String",
          "javaField": "carNo",
          "isPk": "0",
          "isIncrement": "0",
          "isRequired": "1",
          "columnComment": "车牌号"
        }
      ]
    }
  ]
}

Expected:

  • inserts one front_project_table

  • inserts two front_project_column

  • saves one generation record with success = "1"

  • returns SQL containing CREATE TABLE \car_info``

  • Step 7: Add controller endpoint

In FrontProjectController:

@PostMapping("/{projectId}/generate-database")
public AjaxResult generateDatabase(@PathVariable Long projectId, @RequestBody GenerateDatabaseRequest request)
{
    Long userId = SecurityUtils.getUserId();
    return success(aiGenerateService.generateDatabase(userId, projectId, request));
}
  • Step 8: Run tests

Run:

mvn -pl ruoyi-generator -am -Dtest=AiDatabaseSchemaValidatorTest,MysqlDdlBuilderTest,AiGenerateServiceImplTest test

Expected:

Tests run: at least 3, Failures: 0, Errors: 0
  • Step 9: Commit
git add ruoyi-admin/src/main/resources/application.yml `
  ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/config/DeepSeekProperties.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front `
  ruoyi-generator/src/test/java/com/ruoyi/generator/service/front
git commit -m "Add DeepSeek database generation service"

Task 7: Adapt Generator Preview for Front Drafts

Files:

  • Modify: IGenProjectService.java

  • Modify: GenProjectServiceImpl.java

  • Create/modify: IFrontProjectPreviewService.java, FrontProjectPreviewServiceImpl.java

  • Modify: FrontProjectController.java

  • Test: FrontProjectPreviewServiceImplTest.java

  • Step 1: Add generator overloads

In IGenProjectService, add:

List<Map<String, Object>> getProjectStructure(GenProject project, String type);

Map<String, String> previewCode(GenProject project, Long tableId, String keyword, String type);

byte[] downloadStructure(GenProject project, String type);

Existing ID-based methods stay unchanged and delegate to the new object-based overload after loading GenProject.

  • Step 2: Refactor GenProjectServiceImpl

The object overload must not query gen_project by ID. It may still query active templates and project structures.

Template resolution rules:

  1. If a project has a sys_project_template row for project.projectId and type, use it.
  2. Otherwise choose the first active sys_template where template_type = type.
  3. If none exists, throw ServiceException("未配置可用模板:" + type).

Structure query rules:

  • For admin projects, keep existing project template structure lookup.

  • For front draft projects without sys_project_template, use sys_project_structure nodes for the resolved template ID.

  • Expand table-scoped nodes for every GenTable in project.getTables().

  • Step 3: Implement front preview service

IFrontProjectPreviewService:

List<Map<String, Object>> getStructure(Long userId, Long projectId, String templateType);
Map<String, String> getFileContent(Long userId, Long projectId, PreviewFileRequest request);
byte[] download(Long userId, Long projectId, String templateType);

Implementation:

  1. Load FrontProject with tables and columns by user/project ID.
  2. Convert to GenProject.
  3. Call genProjectService.getProjectStructure(genProject, templateType).
  4. Call genProjectService.previewCode(genProject, tableId, category, templateType) for file content.
  5. Call genProjectService.downloadStructure(genProject, templateType) for zip.
  • Step 4: Add preview endpoints

In FrontProjectController:

@PostMapping("/{projectId}/preview")
public AjaxResult preview(@PathVariable Long projectId)

@GetMapping("/{projectId}/structure")
public AjaxResult structure(@PathVariable Long projectId, @RequestParam String templateType)

@GetMapping("/{projectId}/file-content")
public AjaxResult fileContent(@PathVariable Long projectId, PreviewFileRequest request)

@GetMapping("/{projectId}/download")
public void download(@PathVariable Long projectId, @RequestParam String templateType, HttpServletResponse response)

preview marks front_project.preview_status = "1" after structure can be generated for backend, frontend, and admin_frontend.

  • Step 5: Write preview service test

Test:

@Test
public void returnsStructureForDraftProject()
{
    when(frontProjectService.getProject(100L, 200L)).thenReturn(frontProjectWithOneTable());
    when(genProjectService.getProjectStructure(any(GenProject.class), eq("backend")))
            .thenReturn(Collections.singletonList(Collections.singletonMap("label", "backend")));

    List<Map<String, Object>> structure = previewService.getStructure(100L, 200L, "backend");

    assertEquals("backend", structure.get(0).get("label"));
}
  • Step 6: Run tests

Run:

mvn -pl ruoyi-generator -am -Dtest=FrontProjectPreviewServiceImplTest,GenProjectServiceImplTest test

Expected:

Tests run: existing generator tests plus preview test, Failures: 0, Errors: 0
  • Step 7: Commit
git add ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenProjectService.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenProjectServiceImpl.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/IFrontProjectPreviewService.java `
  ruoyi-generator/src/main/java/com/ruoyi/generator/service/front/FrontProjectPreviewServiceImpl.java `
  ruoyi-admin/src/main/java/com/ruoyi/web/controller/front/FrontProjectController.java `
  ruoyi-generator/src/test/java/com/ruoyi/generator/service/front/FrontProjectPreviewServiceImplTest.java
git commit -m "Support front draft project preview"

Task 8: Scaffold Independent easycode-web

Files:

  • Create all easycode-web files listed in File Map.

  • Step 1: Create package and Vite config

easycode-web/package.json:

{
  "name": "easycode-web",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite --host 0.0.0.0",
    "build": "vite build",
    "preview": "vite preview --host 0.0.0.0"
  },
  "dependencies": {
    "@element-plus/icons-vue": "^2.3.1",
    "axios": "^1.7.9",
    "element-plus": "^2.9.1",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.2.1",
    "sass": "^1.83.0",
    "vite": "^6.0.7"
  }
}

easycode-web/vite.config.js:

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())
  return {
    plugins: [vue()],
    server: {
      port: 5174,
      proxy: {
        '/front': {
          target: env.VITE_API_BASE_URL || 'http://localhost:8080',
          changeOrigin: true
        }
      }
    }
  }
})

.env.development:

VITE_API_BASE_URL=http://localhost:8080
  • Step 2: Create router guard

easycode-web/src/router/index.js:

import { createRouter, createWebHistory } from 'vue-router'
import { getToken } from '@/utils/auth'

const routes = [
  { path: '/', name: 'Home', component: () => import('@/views/HomeView.vue') },
  { path: '/login', name: 'Login', component: () => import('@/views/LoginView.vue') },
  { path: '/register', name: 'Register', component: () => import('@/views/RegisterView.vue') },
  { path: '/generate', name: 'Generate', component: () => import('@/views/GenerateView.vue'), meta: { requiresAuth: true } },
  { path: '/project/:projectId/preview', name: 'Preview', component: () => import('@/views/PreviewView.vue'), meta: { requiresAuth: true } }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

router.beforeEach((to) => {
  if (to.meta.requiresAuth && !getToken()) {
    return { path: '/login', query: { redirect: to.fullPath } }
  }
  return true
})

export default router
  • Step 3: Create request wrapper

easycode-web/src/api/request.js:

import axios from 'axios'
import { ElMessage } from 'element-plus'
import { getToken, removeToken } from '@/utils/auth'

const service = axios.create({
  baseURL: '',
  timeout: 60000
})

service.interceptors.request.use((config) => {
  const token = getToken()
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

service.interceptors.response.use(
  (response) => {
    const data = response.data
    if (data && data.code && data.code !== 200) {
      ElMessage.error(data.msg || '请求失败')
      return Promise.reject(new Error(data.msg || '请求失败'))
    }
    return data
  },
  (error) => {
    if (error.response && error.response.status === 401) {
      removeToken()
      ElMessage.error('登录已过期,请重新登录')
    } else {
      ElMessage.error(error.message || '网络异常')
    }
    return Promise.reject(error)
  }
)

export default service
  • Step 4: Create home page

Use the prototype as content reference but write clean Vue and Chinese text. Keep the first viewport as the usable generation entry, not a marketing-only page.

Hero actions:

const startGenerate = () => {
  router.push({
    path: '/generate',
    query: { keyword: keyword.value }
  })
}

Login action:

router.push('/login')
  • Step 5: Create generate page with mock fallback

GenerateView.vue must support these states:

const activeStep = ref(0)
const generating = ref(false)
const projectId = ref(null)
const database = ref({ tables: [], sql: '' })

Buttons:

  • “生成数据库” calls createProject, then generateDatabase.

  • “保存表结构” calls saveDatabase.

  • “预览项目” routes to /project/${projectId}/preview.

  • Step 6: Create preview page

PreviewView.vue loads three structures:

const templateTypes = [
  { label: '后端项目', value: 'backend' },
  { label: '前台前端', value: 'frontend' },
  { label: '后台前端', value: 'admin_frontend' }
]

Clicking a file node calls:

getFileContent(projectId, {
  templateType: activeTemplateType.value,
  nodeId: node.id,
  tableId: node.tableId,
  category: node.category
})
  • Step 7: Install and build

Run:

cd easycode-web
npm install
npm run build

Expected:

vite build succeeds and creates easycode-web/dist

If npm install fails because network access is blocked, rerun with escalation and keep the same command.

  • Step 8: Commit
git add easycode-web
git commit -m "Add EasyCode front web app"

Task 9: Wire Front App to Real APIs

Files:

  • Modify: easycode-web/src/api/auth.js

  • Modify: easycode-web/src/api/project.js

  • Modify: easycode-web/src/views/LoginView.vue

  • Modify: easycode-web/src/views/RegisterView.vue

  • Modify: easycode-web/src/views/GenerateView.vue

  • Modify: easycode-web/src/views/PreviewView.vue

  • Step 1: Implement auth API

auth.js:

import request from './request'

export function login(data) {
  return request.post('/front/auth/login', data)
}

export function register(data) {
  return request.post('/front/auth/register', data)
}

export function profile() {
  return request.get('/front/auth/profile')
}
  • Step 2: Implement project API

project.js:

import request from './request'

function unwrap(res) {
  return res && Object.prototype.hasOwnProperty.call(res, 'data') ? res.data : res
}

export function createProject(data) {
  return request.post('/front/project/create', data).then(unwrap)
}

export function generateDatabase(projectId, data) {
  return request.post(`/front/project/${projectId}/generate-database`, data).then(unwrap)
}

export function getDatabase(projectId) {
  return request.get(`/front/project/${projectId}/database`).then(unwrap)
}

export function saveDatabase(projectId, data) {
  return request.put(`/front/project/${projectId}/database`, data).then(unwrap)
}

export function getStructure(projectId, templateType) {
  return request.get(`/front/project/${projectId}/structure`, { params: { templateType } }).then(unwrap)
}

export function getFileContent(projectId, params) {
  return request.get(`/front/project/${projectId}/file-content`, { params }).then(unwrap)
}
  • Step 3: Use token from login

On successful login:

const res = await login(form)
setToken(res.token)
router.push(route.query.redirect || '/generate')
  • Step 4: Wire generate page

On “生成数据库”:

const project = await createProject({
  projectName: form.projectName,
  projectDesc: form.projectDesc,
  industryTemplate: form.industryTemplate
})
projectId.value = project.projectId
const result = await generateDatabase(projectId.value, form)
database.value = result
activeStep.value = 1

The unwrap helper in project.js normalizes AjaxResult.success(data) to the inner data object.

  • Step 5: Wire preview page

Load structures after route enter:

for (const item of templateTypes) {
  const res = await getStructure(projectId, item.value)
  structures.value[item.value] = res || []
}

Click file node:

const res = await getFileContent(projectId, {
  templateType: activeTemplateType.value,
  nodeId: node.id,
  tableId: node.tableId,
  category: node.category
})
code.value = res && res.content ? res.content : ''
fileName.value = node.label
  • Step 6: Build front app

Run:

cd easycode-web
npm run build

Expected:

vite build succeeds
  • Step 7: Commit
git add easycode-web/src
git commit -m "Connect front web app to workbench APIs"

Task 10: End-to-End Verification and Documentation

Files:

  • Modify: README.md

  • Create or modify: easycode-web/README.md

  • Optional create: sql/front_workbench_test_data.sql

  • Step 1: Add front app README

easycode-web/README.md must include:

# EasyCode Web

## Start

```powershell
npm install
npm run dev

Default URL: http://localhost:5174

Backend

Set VITE_API_BASE_URL in .env.development.

Flow

  1. Register or login front user.
  2. Enter project name and description.
  3. Generate MySQL database design through backend DeepSeek service.
  4. Edit tables and fields.
  5. Preview backend, frontend, and admin frontend project files.
  • Step 2: Update root README

Add a short “前台生成工作台” section with:

### 前台生成工作台

- 前台目录:`easycode-web`
- 后台接口前缀:`/front/**`
- DeepSeek 只在后端 `AiGenerateService` 中调用
- 新增数据库脚本:`sql/front_workbench.sql`
  • Step 3: Backend verification

Run:

mvn -pl ruoyi-admin -am -DfailIfNoTests=false test

Expected:

[INFO] BUILD SUCCESS
  • Step 4: Frontend verification

Run:

cd easycode-web
npm run build

Expected:

vite build succeeds
  • Step 5: Manual smoke test

Start backend and front app. Use these checks:

1. Open http://localhost:5174
2. Click 登录 and register a front user
3. Login redirects back to /generate
4. Create project "车辆管理系统"
5. Click 生成数据库
6. Tables are displayed and editable
7. Save database design
8. Click 预览项目
9. backend/frontend/admin_frontend structures load
10. Click a file and code appears on the right
  • Step 6: Commit
git add README.md easycode-web/README.md
git commit -m "Document front workbench setup"

Final Verification Before Merge

Run from repository root:

git status -sb
git diff --check
mvn -pl ruoyi-admin -am -DfailIfNoTests=false test

Run from easycode-web:

npm run build

Expected:

working tree clean after final commit
git diff --check has no output
Maven BUILD SUCCESS
Vite build succeeds

Implementation Notes

  • Keep DeepSeek API key only in backend environment/config.
  • Do not add DeepSeek calls to easycode-web.
  • Use front token only for /front/**.
  • Do not reuse sys_user for front users.
  • Do not save AI output directly without AiDatabaseSchemaValidator.
  • Keep generated draft data in front_* tables; only convert to GenProject in memory for preview/download.