表单创建和提交

This commit is contained in:
lizhengwei 2026-03-17 14:47:54 +08:00
parent 08b5220225
commit 6f04adaa83
13 changed files with 303 additions and 203 deletions

View File

@ -86,20 +86,47 @@ create index idx_name
create table h5_form_submission
(
id bigint auto_increment comment '主键ID'
id bigint auto_increment comment '主键ID'
primary key,
project_id bigint not null comment 'H5Project的ID',
ip_address bigint unsigned null comment '提交者IP地址',
user_agent varchar(512) null comment '用户代理信息',
form_data json not null comment '表单数据JSON格式存储',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间戳',
update_time datetime null on update CURRENT_TIMESTAMP comment '更新时间戳'
project_id bigint null comment 'H5Project的ID',
templet_form_id bigint null comment '模板表单ID',
form_id bigint null comment '表单ID',
ip_address varchar(50) null comment '提交者IP地址',
user_agent varchar(512) null comment '用户代理信息',
form_data json not null comment '表单数据JSON格式存储',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间戳',
update_time datetime null on update CURRENT_TIMESTAMP comment '更新时间戳',
created_by bigint null comment '创建人'
)
comment '表单提交记录表' charset = utf8mb4;
create index idx_project_id
on h5_form_submission (project_id);
create index idx_templet_form_id
on h5_form_submission (templet_form_id);
create table h5_form
(
id bigint auto_increment comment '主键ID'
primary key,
name varchar(255) null comment '表单名称',
templet_id bigint null comment '模板ID',
content_form_id varchar(50) null comment '内容表单ID',
form_id varchar(50) null comment '表单ID',
auth json null comment '认证信息',
extends json null comment '扩展信息',
fields json null comment '字段信息',
deleted tinyint(1) default 0 not null comment '是否删除',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间戳',
update_time datetime null on update CURRENT_TIMESTAMP comment '更新时间戳',
created_by bigint null comment '创建人'
)
comment 'H5表单定义表' charset = utf8mb4;
create index idx_templet_id
on h5_form (templet_id);
create table h5_group
(
id bigint auto_increment comment '主键ID'
@ -163,4 +190,3 @@ create index idx_group_id
create index idx_project_id
on h5_project_group (project_id);

View File

@ -0,0 +1,31 @@
-- H5表单模块数据库更新脚本
-- 1. 更新 h5_form_submission 表结构
ALTER TABLE h5_form_submission
MODIFY COLUMN project_id BIGINT NULL COMMENT 'H5Project的ID',
ADD COLUMN templet_form_id BIGINT NULL COMMENT '模板表单ID' AFTER project_id,
MODIFY COLUMN form_id VARCHAR(50) NULL COMMENT '表单ID',
MODIFY COLUMN ip_address VARCHAR(50) NULL COMMENT '提交者IP地址',
ADD COLUMN created_by BIGINT NULL COMMENT '创建人' AFTER update_time;
-- 添加索引
CREATE INDEX idx_templet_form_id ON h5_form_submission (templet_form_id);
-- 2. 创建 h5_form 表单定义表
CREATE TABLE IF NOT EXISTS h5_form (
id BIGINT AUTO_INCREMENT COMMENT '主键ID' PRIMARY KEY,
name VARCHAR(255) NULL COMMENT '表单名称',
templet_id BIGINT NULL COMMENT '模板ID',
content_form_id VARCHAR(50) NULL COMMENT '内容表单ID',
form_id VARCHAR(50) NULL COMMENT '表单ID',
auth JSON NULL COMMENT '认证信息',
extends JSON NULL COMMENT '扩展信息',
fields JSON NULL COMMENT '字段信息',
deleted TINYINT(1) DEFAULT 0 NOT NULL COMMENT '是否删除',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间戳',
update_time DATETIME NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间戳',
created_by BIGINT NULL COMMENT '创建人'
) COMMENT 'H5表单定义表' CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC;
-- 添加索引
CREATE INDEX idx_templet_id ON h5_form (templet_id);

View File

@ -3,7 +3,6 @@ package com.luban.api.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.service.H5FormService;
import com.luban.api.util.JSON;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
@ -20,13 +19,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 表单控制器
* </p>
*
* @author your-name
*/
@RestController
@RequestMapping("/form")
@Tag(name = "表单管理", description = "表单提交与查询")
@ -36,35 +28,22 @@ public class H5FormController {
@Autowired
private H5FormService formService;
@Autowired
private JSON json;
/**
* save表单版本+1
*/
@Operation(summary = "save表单版本+1")
@Operation(summary = "保存表单版本")
@PostMapping("/saveForm")
public Boolean saveForm(@RequestBody SubmitFormRequest request, HttpServletRequest httpRequest) {
// 获取客户端IP地址
String ipAddress = ServletUtils.getClientIP();
// 获取User-Agent
String userAgent = httpRequest.getHeader("User-Agent");
H5FormSubmissionDTO dto = new H5FormSubmissionDTO();
// 从请求中获取formId和templetFormId
dto.setFormId(request.getFormId());
dto.setTempletFormId(request.getTemplateFormId());
// 如果formData为空初始化为Map
Map<String, Object> formData = request.getFormData();
if (formData == null) {
formData = new HashMap<>();
}
// 添加版本信息到formData
// 版本号可以基于当前时间戳或自增计数器
long version = System.currentTimeMillis() / 1000; // 使用时间戳作为版本号
long version = System.currentTimeMillis() / 1000;
formData.put("version", version);
formData.put("saveTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
@ -75,19 +54,10 @@ public class H5FormController {
return formService.submitFormData(dto);
}
/**
* 提交表单数据
*
* @param request 请求体包含表单数据
* @return 是否提交成功
*/
@Operation(summary = "提交表单数据")
@PostMapping("/submit")
public Boolean submitForm(@RequestBody SubmitFormRequest request, HttpServletRequest httpRequest) {
// 获取客户端IP地址
String ipAddress = ServletUtils.getClientIP();
// 获取User-Agent
String userAgent = httpRequest.getHeader("User-Agent");
H5FormSubmissionDTO dto = new H5FormSubmissionDTO();
@ -111,12 +81,6 @@ public class H5FormController {
return formService.submitFormData(dto);
}
/**
* 根据templateFormId获取所有表单数据
*
* @param templateFormId templateFormId
* @return 表单数据列表
*/
@Operation(summary = "分页查询提交数据")
@GetMapping("/list/detail/page")
public IPage<H5FormSubmissionDTO> listDetailPage(@RequestParam Long templateFormId,
@ -153,10 +117,7 @@ public class H5FormController {
]
}
""";
return formService.listDetailPage(templateFormId,
orderBy,
pageNum,
pageSize);
return formService.listDetailPage(templateFormId, orderBy, pageNum, pageSize);
}
@Operation(summary = "获取所有表单")

View File

@ -10,19 +10,8 @@ import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 表单提交转换器
*
* @author your-name
*/
public class FormSubmissionConvert {
/**
* 将FormSubmission实体转换为H5FormSubmissionDTO
*
* @param entity FormSubmission实体
* @return H5FormSubmissionDTO
*/
public static H5FormSubmissionDTO toDTO(FormSubmission entity) {
if (entity == null) {
return null;
@ -34,30 +23,16 @@ public class FormSubmissionConvert {
if (entity.getFormData() != null) {
dto.setFormData(JSONUtil.toBean(entity.getFormData(), java.util.Map.class));
}
// 将IP地址转换为字符串
dto.setIpAddress(entity.getIpAddress());
return dto;
}
/**
* 将FormSubmission实体列表转换为H5FormSubmissionDTO列表
*
* @param entities FormSubmission实体列表
* @return H5FormSubmissionDTO列表
*/
public static List<H5FormSubmissionDTO> toDTO(List<FormSubmission> entities) {
List<H5FormSubmissionDTO> dtoList = new ArrayList<>();
if (entities == null) {
return dtoList;
if (entities == null || entities.isEmpty()) {
return new ArrayList<>();
}
for (FormSubmission entity : entities) {
dtoList.add(toDTO(entity));
}
return dtoList;
return entities.stream().map(FormSubmissionConvert::toDTO).toList();
}
public static IPage<H5FormSubmissionDTO> toPageDTO(IPage<FormSubmission> page) {
IPage<H5FormSubmissionDTO> newPage = new Page<>();
newPage.setPages(page.getPages());
@ -68,12 +43,6 @@ public class FormSubmissionConvert {
return newPage;
}
/**
* 将H5FormSubmissionDTO转换为FormSubmission实体
*
* @param dto H5FormSubmissionDTO
* @return FormSubmission实体
*/
public static FormSubmission toEntity(H5FormSubmissionDTO dto) {
if (dto == null) {
return null;

View File

@ -0,0 +1,64 @@
package com.luban.api.convert;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.entity.FormSubmission;
import com.luban.api.entity.H5Form;
import java.util.List;
import java.util.Map;
public class H5FormConvert {
public static H5FormSubmissionDTO toDTO(FormSubmission entity) {
if (entity == null) {
return null;
}
H5FormSubmissionDTO dto = new H5FormSubmissionDTO();
dto.setId(entity.getId());
dto.setProjectId(entity.getProjectId());
dto.setTempletFormId(entity.getTempletFormId());
dto.setFormId(entity.getFormId());
dto.setIpAddress(entity.getIpAddress());
dto.setUserAgent(entity.getUserAgent());
dto.setCreateTime(entity.getCreateTime());
dto.setUpdateTime(entity.getUpdateTime());
if (entity.getFormData() != null) {
dto.setFormData(JSONUtil.toBean(entity.getFormData(), Map.class));
}
return dto;
}
public static H5FormSubmissionDTO toDTO(H5Form entity) {
if (entity == null) {
return null;
}
H5FormSubmissionDTO dto = new H5FormSubmissionDTO();
dto.setId(entity.getId());
dto.setName(entity.getName());
dto.setTempletId(entity.getTempletId());
dto.setContentFormId(entity.getContentFormId());
dto.setFormId(Long.valueOf(entity.getFormId()));
dto.setCreateTime(entity.getCreateTime());
dto.setUpdateTime(entity.getUpdateTime());
dto.setDeleted(entity.getDeleted());
if (entity.getAuth() != null) {
dto.setAuth(JSONUtil.toBean(entity.getAuth(), Map.class));
}
if (entity.getExtendsInfo() != null) {
dto.setExtendsInfo(JSONUtil.toBean(entity.getExtendsInfo(), Map.class));
}
if (entity.getFields() != null) {
dto.setFields(JSONUtil.toBean(entity.getFields(), Map.class));
}
return dto;
}
public static List<H5FormSubmissionDTO> toDTOList(List<H5Form> entities) {
if (entities == null || entities.isEmpty()) {
return List.of();
}
return entities.stream().map(H5FormConvert::toDTO).toList();
}
}

View File

@ -0,0 +1,9 @@
package com.luban.api.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.luban.api.entity.H5Form;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface H5FormDao extends BaseMapper<H5Form> {
}

View File

@ -3,50 +3,57 @@ package com.luban.api.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;
/**
* H5表单提交数据传输对象
*
* @author your-name
*/
@Data
@Schema(title = "H5表单提交对象")
public class H5FormSubmissionDTO {
/**
* 项目ID
*/
@Schema(title = "主键ID")
private Long id;
@Schema(title = "项目ID")
private Long projectId;
/**
* 模板表单ID
*/
@Schema(title = "模板表单ID")
private Long templetFormId;
/**
* 表单ID
*/
@Schema(title = "表单ID")
private Long formId;
/**
* 表单数据
*/
@Schema(title = "表单数据")
private Map<String, Object> formData;
/**
* IP地址
*/
@Schema(title = "IP地址")
private String ipAddress;
/**
* 用户代理
*/
@Schema(title = "用户代理")
private String userAgent;
@Schema(title = "创建时间")
private LocalDateTime createTime;
@Schema(title = "更新时间")
private LocalDateTime updateTime;
@Schema(title = "表单名称")
private String name;
@Schema(title = "模板ID")
private Long templetId;
@Schema(title = "内容表单ID")
private String contentFormId;
@Schema(title = "认证信息")
private Map<String, Object> auth;
@Schema(title = "扩展信息")
private Map<String, Object> extendsInfo;
@Schema(title = "字段信息")
private Map<String, String> fields;
@Schema(title = "是否删除")
private Integer deleted;
}

View File

@ -10,11 +10,6 @@ import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 表单提交记录实体类
*
* @author your-name
*/
@Data
@EqualsAndHashCode
@Accessors(chain = true)
@ -25,60 +20,33 @@ public class FormSubmission implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* H5Project的ID
*/
@TableField("project_id")
private Long projectId;
/**
* 模板表单ID
*/
@TableField("templet_form_id")
private Long templetFormId;
/**
* 表单ID
*/
@TableField("form_id")
private String formId;
private Long formId;
/**
* 提交者IP地址
*/
@TableField("ip_address")
private String ipAddress;
/**
* 用户代理信息
*/
@TableField("user_agent")
private String userAgent;
/**
* 表单数据JSON格式存储
*/
@TableField("form_data")
private String formData;
/**
* 创建时间戳
*/
@TableField("create_time")
private LocalDateTime createTime;
/**
* 更新时间戳
*/
@TableField("update_time")
private LocalDateTime updateTime;
@TableField("created_by")
private Long createdBy;
}

View File

@ -0,0 +1,59 @@
package com.luban.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode
@Accessors(chain = true)
@TableName("h5_form")
@Schema(title = "H5表单定义")
public class H5Form implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("name")
private String name;
@TableField("templet_id")
private Long templetId;
@TableField("content_form_id")
private String contentFormId;
@TableField("form_id")
private String formId;
@TableField("auth")
private String auth;
@TableField("extends")
private String extendsInfo;
@TableField("fields")
private String fields;
@TableField("deleted")
@TableLogic
private Integer deleted;
@TableField("create_time")
private LocalDateTime createTime;
@TableField("update_time")
private LocalDateTime updateTime;
@TableField("created_by")
private Long createdBy;
}

View File

@ -0,0 +1,13 @@
package com.luban.api.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.luban.api.entity.H5Form;
import java.util.List;
public interface H5FormDefinitionService extends IService<H5Form> {
List<H5Form> listByTempletId(Long templetId, Boolean hasDeleted);
H5Form getByTempletFormId(Long templetFormId);
}

View File

@ -0,0 +1,37 @@
package com.luban.api.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luban.api.dao.H5FormDao;
import com.luban.api.entity.H5Form;
import com.luban.api.service.H5FormDefinitionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class H5FormDefinitionServiceImpl extends ServiceImpl<H5FormDao, H5Form> implements H5FormDefinitionService {
@Override
public List<H5Form> listByTempletId(Long templetId, Boolean hasDeleted) {
LambdaQueryWrapper<H5Form> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(H5Form::getTempletId, templetId);
if (hasDeleted != null && hasDeleted) {
wrapper.eq(H5Form::getDeleted, 1);
} else {
wrapper.eq(H5Form::getDeleted, 0);
}
wrapper.orderByDesc(H5Form::getCreateTime);
return this.list(wrapper);
}
@Override
public H5Form getByTempletFormId(Long templetFormId) {
LambdaQueryWrapper<H5Form> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(H5Form::getId, templetFormId);
wrapper.eq(H5Form::getDeleted, 0);
return this.getOne(wrapper);
}
}

View File

@ -5,10 +5,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dromara.md.form.service.FormService;
import com.luban.api.convert.FormSubmissionConvert;
import com.luban.api.convert.H5FormConvert;
import com.luban.api.dao.FormSubmissionDao;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.dto.H5ProjectContentDTO;
import com.luban.api.entity.FormSubmission;
import com.luban.api.entity.H5Form;
import com.luban.api.service.H5FormDefinitionService;
import com.luban.api.service.H5FormService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -18,13 +21,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 表单服务实现类
* </p>
*
* @author lizhw
*/
@Slf4j
@Service
@RequiredArgsConstructor
@ -32,6 +28,7 @@ public class H5FormServiceImpl extends ServiceImpl<FormSubmissionDao, FormSubmis
private final FormSubmissionDao formSubmissionDao;
private final FormService formService;
private final H5FormDefinitionService h5FormDefinitionService;
@Override
@Transactional(rollbackFor = Exception.class)
@ -41,7 +38,6 @@ public class H5FormServiceImpl extends ServiceImpl<FormSubmissionDao, FormSubmis
return this.save(entity);
}
@Override
public IPage<H5FormSubmissionDTO> listDetailPage(Long templateFormId, String orderBy, Integer pageNum, Integer pageSize) {
Page<FormSubmission> page = this.lambdaQuery()
@ -53,61 +49,36 @@ public class H5FormServiceImpl extends ServiceImpl<FormSubmissionDao, FormSubmis
@Override
public long saveForm(H5ProjectContentDTO content) {
// 步骤1: 获取所有form组件
List<H5ProjectContentDTO.FormElement> formElements = extractFormElements(content);
// 步骤2: 保存所有form组件
saveFormElements(formElements);
return formElements.size();
}
/**
* 步骤1: 从H5项目内容中提取所有表单组件
*
* @param content H5项目内容
* @return 表单组件列表
*/
private List<H5ProjectContentDTO.FormElement> extractFormElements(H5ProjectContentDTO content) {
List<H5ProjectContentDTO.FormElement> formElements = new ArrayList<>();
if (content == null || content.getLayouts() == null) {
return formElements;
}
// 遍历所有布局提取表单元素
for (H5ProjectContentDTO.Layout layout : content.getLayouts()) {
if (layout.getElements() != null) {
extractFormElementsFromList(layout.getElements(), formElements);
}
}
return formElements;
}
/**
* 从元素列表中递归提取表单组件
*
* @param elements 元素列表
* @param formElements 收集到的表单组件列表
*/
private void extractFormElementsFromList(List<H5ProjectContentDTO.Element> elements,
List<H5ProjectContentDTO.FormElement> formElements) {
if (elements == null || elements.isEmpty()) {
return;
}
for (H5ProjectContentDTO.Element element : elements) {
if (element == null) {
continue;
}
// 如果是表单元素添加到结果列表
if (H5ProjectContentDTO.ElementType.FORM.equals(element.getType())) {
formElements.add((H5ProjectContentDTO.FormElement) element);
}
// 递归处理组元素中的子元素
if (element instanceof H5ProjectContentDTO.GroupElement) {
H5ProjectContentDTO.GroupElement groupElement = (H5ProjectContentDTO.GroupElement) element;
if (groupElement.getElements() != null) {
@ -117,58 +88,38 @@ public class H5FormServiceImpl extends ServiceImpl<FormSubmissionDao, FormSubmis
}
}
/**
* 步骤2: 保存表单组件列表
*
* @param formElements 表单组件列表
*/
private void saveFormElements(List<H5ProjectContentDTO.FormElement> formElements) {
if (formElements == null || formElements.isEmpty()) {
return;
}
for (H5ProjectContentDTO.FormElement formElement : formElements) {
saveSingleFormElement(formElement);
}
}
/**
* 保存单个表单组件
*
* @param formElement 表单组件
*/
private void saveSingleFormElement(H5ProjectContentDTO.FormElement formElement) {
if (formElement == null) {
return;
}
// 记录表单组件信息到日志
log.info("发现表单组件: UUID={}, Title={}, FormSchema={}, FormData={}",
formElement.getUuid(),
formElement.getTitle(),
formElement.getFormSchema(),
formElement.getFormData());
// formService.create()
// TODO: 这里可以根据实际需求决定是否需要保存到数据库
// 如果需要保存到数据库可以创建相应的实体类和服务
}
@Override
public List<H5FormSubmissionDTO> getEditorForms(Long templateId, Boolean hasDeleted) {
// 由于当前模块没有表单定义表返回空列表作为占位符
// 实际实现应查询 ruoyi-form 模块中的表单数据
return new ArrayList<>();
List<H5Form> h5Forms = h5FormDefinitionService.listByTempletId(templateId, hasDeleted);
return H5FormConvert.toDTOList(h5Forms);
}
@Override
public List<H5FormSubmissionDTO> getEditorTemplateForms(Long templateFormId) {
// 由于当前模块没有表单定义表返回空列表作为占位符
// 实际实现应查询 ruoyi-form 模块中的表单数据
return new ArrayList<>();
H5Form h5Form = h5FormDefinitionService.getByTempletFormId(templateFormId);
if (h5Form == null) {
return new ArrayList<>();
}
return List.of(H5FormConvert.toDTO(h5Form));
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.luban.api.dao.H5FormDao">
</mapper>