支持表单提交

This commit is contained in:
lizhengwei 2025-11-17 18:25:31 +08:00
parent e043d39aec
commit f45169db3a
14 changed files with 348 additions and 263 deletions

View File

@ -0,0 +1,13 @@
CREATE TABLE `h5_form_submission`
(
id bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
project_id bigint NOT NULL COMMENT 'H5Project的ID',
ip_address bigint UNSIGNED DEFAULT NULL COMMENT '提交者IP地址',
user_agent varchar(512) DEFAULT NULL COMMENT '用户代理信息',
form_data JSON NOT NULL COMMENT '表单数据JSON格式存储',
create_time bigint not null comment '创建时间戳',
update_time bigint not null comment '更新时间戳',
PRIMARY KEY (id),
KEY `idx_project_id` (project_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='表单提交记录表';

View File

@ -0,0 +1,24 @@
### 提交表单数据
POST {{host}}/h5/form/submit
Content-Type: application/json
{
"projectId": 1,
"formData": {
"name": "张三",
"age": 18,
"email": "zhangsan@example.com"
}
}
### 根据项目ID分页查询表单提交记录
GET {{host}}/h5/form/page/project/1?pageNum=1&pageSize=10
### 根据项目ID分页查询表单提交记录(带过滤条件)
GET {{host}}/h5/form/page/project/1?pageNum=1&pageSize=10&name=张三
### 根据提交记录ID获取表单数据详情
GET {{host}}/h5/form/detail/1
### 根据项目ID获取所有表单数据
GET {{host}}/h5/form/list/project/1

View File

@ -0,0 +1,5 @@
{
"dev": {
"host": "http://localhost:19200"
}
}

View File

@ -1,9 +1,10 @@
package com.luban.api.controller; package com.luban.api.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.entity.FormSubmission; import com.luban.api.entity.FormSubmission;
import com.luban.api.mongo.entity.FormData;
import com.luban.api.service.FormService; import com.luban.api.service.FormService;
import com.luban.api.util.JSON;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -32,6 +33,9 @@ public class FormController {
@Autowired @Autowired
private FormService formService; private FormService formService;
@Autowired
private JSON json;
/** /**
* 提交表单数据 * 提交表单数据
* *
@ -46,47 +50,31 @@ public class FormController {
// 获取User-Agent // 获取User-Agent
String userAgent = httpRequest.getHeader("User-Agent"); String userAgent = httpRequest.getHeader("User-Agent");
return formService.submitFormData( H5FormSubmissionDTO dto = new H5FormSubmissionDTO();
request.getTemplateFormId(), dto.setProjectId(request.getProjectId());
request.getFormId(), dto.setFormData(json.toJSON(request.getFormData()));
request.getFormData(), dto.setIpAddress(JSON.ipToLong(ipAddress));
ipAddress, dto.setUserAgent(userAgent);
userAgent return formService.submitFormData(dto);
);
} }
/** /**
* 根据模板表单ID分页查询表单提交记录 * 根据项目ID分页查询表单提交记录
* *
* @param templateFormId 模板表单ID * @param projectId 项目ID
* @param fieldFilters 字段过滤条件
* @param pageNum 页码 * @param pageNum 页码
* @param pageSize 每页大小 * @param pageSize 每页大小
* @return 表单提交记录分页数据 * @return 表单提交记录分页数据
*/ */
@Operation(summary = "根据模板表单ID分页查询表单提交记录") @Operation(summary = "根据项目ID分页查询表单提交记录")
@GetMapping("/page/template/{templateFormId}") @PostMapping("/page/project/{projectId}")
public IPage<FormSubmission> pageByTemplateFormId( public IPage<FormSubmission> pageByProjectId(
@PathVariable Long templateFormId, @PathVariable Long projectId,
@RequestParam(defaultValue = "1") Integer pageNum, @RequestBody(required = false) Map<String, Object> fieldFilters,
@RequestParam(defaultValue = "10") Integer pageSize) { @RequestParam(defaultValue = "1") Integer pageNum,
return formService.pageFormSubmissionsByTemplateFormId(templateFormId, pageNum, pageSize); @RequestParam(defaultValue = "10") Integer pageSize) {
} return formService.pageFormSubmissionsByProjectId(projectId, fieldFilters, pageNum, pageSize);
/**
* 根据表单ID分页查询表单提交记录
*
* @param formId 表单ID
* @param pageNum 页码
* @param pageSize 每页大小
* @return 表单提交记录分页数据
*/
@Operation(summary = "根据表单ID分页查询表单提交记录")
@GetMapping("/page/form/{formId}")
public IPage<FormSubmission> pageByFormId(
@PathVariable String formId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
return formService.pageFormSubmissionsByFormId(formId, pageNum, pageSize);
} }
/** /**
@ -97,38 +85,26 @@ public class FormController {
*/ */
@Operation(summary = "根据提交记录ID获取表单数据详情") @Operation(summary = "根据提交记录ID获取表单数据详情")
@GetMapping("/detail/{submissionId}") @GetMapping("/detail/{submissionId}")
public FormData getFormDataDetail(@PathVariable Long submissionId) { public H5FormSubmissionDTO getFormDataDetail(@PathVariable Long submissionId) {
return formService.getFormDataDetail(submissionId); return formService.getFormDataDetail(submissionId);
} }
/** /**
* 根据模板表单ID获取所有表单数据 * 根据项目ID获取所有表单数据
* *
* @param templateFormId 模板表单ID * @param projectId 项目ID
* @return 表单数据列表 * @return 表单数据列表
*/ */
@Operation(summary = "根据模板表单ID获取所有表单数据") @Operation(summary = "根据项目ID获取所有表单数据")
@GetMapping("/list/template/{templateFormId}") @GetMapping("/list/project/{projectId}")
public List<FormData> listByTemplateFormId(@PathVariable Long templateFormId) { public List<H5FormSubmissionDTO> listByProjectId(@PathVariable Long projectId) {
return formService.listFormDataByTemplateFormId(templateFormId); return formService.listFormDataByProjectId(projectId);
}
/**
* 根据表单ID获取所有表单数据
*
* @param formId 表单ID
* @return 表单数据列表
*/
@Operation(summary = "根据表单ID获取所有表单数据")
@GetMapping("/list/form/{formId}")
public List<FormData> listByFormId(@PathVariable String formId) {
return formService.listFormDataByFormId(formId);
} }
/** /**
* 获取客户端IP地址 * 获取客户端IP地址
* *
* @param request HTTP请求 * @param request HttpServletRequest对象
* @return 客户端IP地址 * @return 客户端IP地址
*/ */
private String getClientIpAddress(HttpServletRequest request) { private String getClientIpAddress(HttpServletRequest request) {
@ -145,26 +121,10 @@ public class FormController {
return ipAddress; return ipAddress;
} }
/**
* 提交表单请求类
*/
@Setter
@Getter @Getter
@Setter
public static class SubmitFormRequest { public static class SubmitFormRequest {
/** private Long projectId;
* 模板表单ID
*/
private Long templateFormId;
/**
* 表单ID
*/
private String formId;
/**
* 表单数据 key为字段idvalue为字段值
*/
private Map<String, Object> formData; private Map<String, Object> formData;
} }
} }

View File

@ -0,0 +1,67 @@
package com.luban.api.convert;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.entity.FormSubmission;
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;
}
H5FormSubmissionDTO dto = new H5FormSubmissionDTO();
BeanUtils.copyProperties(entity, dto);
// 将数字形式的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;
}
for (FormSubmission entity : entities) {
dtoList.add(toDTO(entity));
}
return dtoList;
}
/**
* 将H5FormSubmissionDTO转换为FormSubmission实体
*
* @param dto H5FormSubmissionDTO
* @return FormSubmission实体
*/
public static FormSubmission toEntity(H5FormSubmissionDTO dto) {
if (dto == null) {
return null;
}
FormSubmission entity = new FormSubmission();
BeanUtils.copyProperties(dto, entity);
return entity;
}
}

View File

@ -1,15 +1,30 @@
package com.luban.api.dao; package com.luban.api.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.luban.api.entity.FormSubmission; import com.luban.api.entity.FormSubmission;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Map;
/** /**
* <p> * 表单提交记录 Mapper接口
* 表单提交记录 Mapper 接口
* </p>
* *
* @author your-name * @author your-name
*/ */
@Mapper
public interface FormSubmissionDao extends BaseMapper<FormSubmission> { public interface FormSubmissionDao extends BaseMapper<FormSubmission> {
/**
* 根据项目ID和字段过滤条件分页查询表单提交记录
*
* @param page 分页对象
* @param projectId 项目ID
* @param fieldFilters 字段过滤条件
* @return 表单提交记录分页数据
*/
IPage<FormSubmission> selectByProjectIdWithFieldFilters(
IPage<FormSubmission> page,
@Param("projectId") Long projectId,
@Param("fieldFilters") Map<String, Object> fieldFilters);
} }

View File

@ -0,0 +1,47 @@
package com.luban.api.dto;
import lombok.Data;
/**
* 表单数据传输对象
*
* @author your-name
*/
@Data
public class H5FormSubmissionDTO {
/**
* 主键ID
*/
private Long id;
/**
* H5Project的ID
*/
private Long projectId;
/**
* 提交者IP地址(IPv4为数字存储)
*/
private Long ipAddress;
/**
* 用户代理信息
*/
private String userAgent;
/**
* 表单数据JSON格式存储
*/
private String formData;
/**
* 创建时间戳
*/
private Long createTime;
/**
* 更新时间戳
*/
private Long updateTime;
}

View File

@ -31,27 +31,38 @@ public class FormSubmission implements Serializable {
private Long id; private Long id;
/** /**
* 模板表单ID对应H5Project的ID * H5Project的ID
*/ */
private Long templateFormId; @TableField("project_id")
private Long projectId;
/**
* 表单ID实例ID
*/
private String formId;
/**
* 提交时间戳
*/
private Long submitTime;
/** /**
* 提交者IP地址 * 提交者IP地址
*/ */
private String ipAddress; @TableField("ip_address")
private Long ipAddress;
/** /**
* 用户代理信息 * 用户代理信息
*/ */
@TableField("user_agent")
private String userAgent; private String userAgent;
/**
* 表单数据JSON格式存储
*/
@TableField("form_data")
private String formData;
/**
* 创建时间戳
*/
@TableField("create_time")
private Long createTime;
/**
* 更新时间戳
*/
@TableField("update_time")
private Long updateTime;
} }

View File

@ -1,30 +0,0 @@
package com.luban.api.mongo.dao;
import com.luban.api.mongo.entity.FormData;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
/**
* 表单数据 Repository
*
* @author lizhw
*/
public interface FormDataRepository extends MongoRepository<FormData, Long> {
/**
* 根据模板表单ID查询表单数据列表
*
* @param templateFormId 模板表单ID
* @return 表单数据列表
*/
List<FormData> findByTemplateFormId(Long templateFormId);
/**
* 根据表单ID查询表单数据列表
*
* @param formId 表单ID
* @return 表单数据列表
*/
List<FormData> findByFormId(String formId);
}

View File

@ -1,58 +0,0 @@
package com.luban.api.mongo.entity;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Map;
/**
* 表单数据实体类 (存储在MongoDB中)
*
* @author your-name
*/
@Data
@Document(collection = "h5_form_data")
public class FormData {
/**
* 主键ID
*/
@Id
private Long id;
/**
* 提交记录ID对应MySQL中FormSubmission的ID
*/
private Long submissionId;
/**
* 模板表单ID对应H5Project的ID
*/
private Long templateFormId;
/**
* 表单ID实例ID
*/
private String formId;
/**
* 表单数据key为formItem的propvalue为用户填写的内容
*/
private Map<String, Object> formData;
/**
* 提交时间戳
*/
private Long submitTime;
/**
* 提交者IP地址
*/
private String ipAddress;
/**
* 用户代理信息
*/
private String userAgent;
}

View File

@ -2,8 +2,8 @@ package com.luban.api.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.entity.FormSubmission; import com.luban.api.entity.FormSubmission;
import com.luban.api.mongo.entity.FormData;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -20,34 +20,21 @@ public interface FormService extends IService<FormSubmission> {
/** /**
* 提交表单数据 * 提交表单数据
* *
* @param templateFormId 模板表单ID * @param dto 表单数据传输对象
* @param formId 表单ID
* @param formData 表单数据
* @param ipAddress IP地址
* @param userAgent 用户代理
* @return 是否提交成功 * @return 是否提交成功
*/ */
boolean submitFormData(Long templateFormId, String formId, Map<String, Object> formData, String ipAddress, String userAgent); boolean submitFormData(H5FormSubmissionDTO dto);
/** /**
* 根据模板表单ID分页查询表单提交记录 * 根据项目ID分页查询表单提交记录支持字段过滤
* *
* @param templateFormId 模板表单ID * @param projectId 项目ID
* @param fieldFilters 字段过滤条件
* @param pageNum 页码 * @param pageNum 页码
* @param pageSize 每页大小 * @param pageSize 每页大小
* @return 表单提交记录分页数据 * @return 表单提交记录分页数据
*/ */
IPage<FormSubmission> pageFormSubmissionsByTemplateFormId(Long templateFormId, Integer pageNum, Integer pageSize); IPage<FormSubmission> pageFormSubmissionsByProjectId(Long projectId, Map<String, Object> fieldFilters, Integer pageNum, Integer pageSize);
/**
* 根据表单ID分页查询表单提交记录
*
* @param formId 表单ID
* @param pageNum 页码
* @param pageSize 每页大小
* @return 表单提交记录分页数据
*/
IPage<FormSubmission> pageFormSubmissionsByFormId(String formId, Integer pageNum, Integer pageSize);
/** /**
* 根据提交记录ID获取表单数据详情 * 根据提交记录ID获取表单数据详情
@ -55,21 +42,14 @@ public interface FormService extends IService<FormSubmission> {
* @param submissionId 提交记录ID * @param submissionId 提交记录ID
* @return 表单数据详情 * @return 表单数据详情
*/ */
FormData getFormDataDetail(Long submissionId); H5FormSubmissionDTO getFormDataDetail(Long submissionId);
/** /**
* 根据模板表单ID获取所有表单数据 * 根据项目ID获取所有表单数据
* *
* @param templateFormId 模板表单ID * @param projectId 项目ID
* @return 表单数据列表 * @return 表单数据列表
*/ */
List<FormData> listFormDataByTemplateFormId(Long templateFormId); List<H5FormSubmissionDTO> listFormDataByProjectId(Long projectId);
/**
* 根据表单ID获取所有表单数据
*
* @param formId 表单ID
* @return 表单数据列表
*/
List<FormData> listFormDataByFormId(String formId);
} }

View File

@ -1,15 +1,13 @@
package com.luban.api.service.impl; package com.luban.api.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luban.api.convert.FormSubmissionConvert;
import com.luban.api.dao.FormSubmissionDao; import com.luban.api.dao.FormSubmissionDao;
import com.luban.api.dto.H5FormSubmissionDTO;
import com.luban.api.entity.FormSubmission; import com.luban.api.entity.FormSubmission;
import com.luban.api.mongo.dao.FormDataRepository;
import com.luban.api.mongo.entity.FormData;
import com.luban.api.service.FormService; import com.luban.api.service.FormService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -26,68 +24,43 @@ import java.util.Map;
@Service @Service
public class FormServiceImpl extends ServiceImpl<FormSubmissionDao, FormSubmission> implements FormService { public class FormServiceImpl extends ServiceImpl<FormSubmissionDao, FormSubmission> implements FormService {
@Autowired private final FormSubmissionDao formSubmissionDao;
private FormDataRepository formDataRepository;
public FormServiceImpl(FormSubmissionDao formSubmissionDao) {
this.formSubmissionDao = formSubmissionDao;
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean submitFormData(Long templateFormId, String formId, Map<String, Object> formData, String ipAddress, String userAgent) { public boolean submitFormData(H5FormSubmissionDTO dto) {
// 保存到MySQL // 转换DTO为实体并保存到MySQL
FormSubmission formSubmission = new FormSubmission(); FormSubmission entity = FormSubmissionConvert.toEntity(dto);
formSubmission.setTemplateFormId(templateFormId); // 设置创建和更新时间
formSubmission.setFormId(formId);
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
formSubmission.setSubmitTime(currentTime); entity.setId(null);
formSubmission.setIpAddress(ipAddress); entity.setCreateTime(currentTime);
formSubmission.setUserAgent(userAgent); entity.setUpdateTime(currentTime);
this.save(formSubmission); return this.save(entity);
// 保存到MongoDB
FormData formDataEntity = new FormData();
formDataEntity.setSubmissionId(formSubmission.getId());
formDataEntity.setTemplateFormId(templateFormId);
formDataEntity.setFormId(formId);
formDataEntity.setFormData(formData);
formDataEntity.setSubmitTime(currentTime);
formDataEntity.setIpAddress(ipAddress);
formDataEntity.setUserAgent(userAgent);
formDataRepository.save(formDataEntity);
return true;
} }
@Override @Override
public IPage<FormSubmission> pageFormSubmissionsByTemplateFormId(Long templateFormId, Integer pageNum, Integer pageSize) { public IPage<FormSubmission> pageFormSubmissionsByProjectId(Long projectId, Map<String, Object> fieldFilters, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<FormSubmission> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FormSubmission::getTemplateFormId, templateFormId);
queryWrapper.orderByDesc(FormSubmission::getSubmitTime);
IPage<FormSubmission> page = new Page<>(pageNum, pageSize); IPage<FormSubmission> page = new Page<>(pageNum, pageSize);
return this.page(page, queryWrapper); return formSubmissionDao.selectByProjectIdWithFieldFilters(page, projectId, fieldFilters);
} }
@Override @Override
public IPage<FormSubmission> pageFormSubmissionsByFormId(String formId, Integer pageNum, Integer pageSize) { public H5FormSubmissionDTO getFormDataDetail(Long submissionId) {
LambdaQueryWrapper<FormSubmission> queryWrapper = new LambdaQueryWrapper<>(); FormSubmission formSubmission = this.getById(submissionId);
queryWrapper.eq(FormSubmission::getFormId, formId); return FormSubmissionConvert.toDTO(formSubmission);
queryWrapper.orderByDesc(FormSubmission::getSubmitTime);
IPage<FormSubmission> page = new Page<>(pageNum, pageSize);
return this.page(page, queryWrapper);
} }
@Override @Override
public FormData getFormDataDetail(Long submissionId) { public List<H5FormSubmissionDTO> listFormDataByProjectId(Long projectId) {
return formDataRepository.findById(submissionId).orElse(null); List<FormSubmission> formSubmissions = this.lambdaQuery()
.eq(FormSubmission::getProjectId, projectId)
.list();
return FormSubmissionConvert.toDTO(formSubmissions);
} }
@Override
public List<FormData> listFormDataByTemplateFormId(Long templateFormId) {
return formDataRepository.findByTemplateFormId(templateFormId);
}
@Override
public List<FormData> listFormDataByFormId(String formId) {
return formDataRepository.findByFormId(formId);
}
} }

View File

@ -9,6 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -63,4 +66,62 @@ public class JSON {
} }
return null; return null;
} }
/**
* 将IP地址字符串转换为数字形式
*
* @param ipAddress IP地址字符串 "192.168.1.1"
* @return IP地址的数字表示形式
*/
public static Long ipToLong(String ipAddress) {
if (ipAddress == null || ipAddress.isEmpty()) {
return null;
}
try {
InetAddress inetAddress = InetAddress.getByName(ipAddress);
byte[] bytes = inetAddress.getAddress();
if (bytes.length == 4) {
// IPv4
long result = 0;
for (byte b : bytes) {
result = result << 8 | (b & 0xFF);
}
return result;
} else {
// IPv6 (简化处理)
return new BigInteger(bytes).longValue();
}
} catch (UnknownHostException e) {
log.error("IP地址转换异常: " + ipAddress, e);
return null;
}
}
/**
* 将数字形式的IP地址转换为字符串
*
* @param ipLong 数字形式的IP地址
* @return IP地址字符串
*/
public static String longToIp(Long ipLong) {
if (ipLong == null) {
return null;
}
try {
byte[] bytes = new byte[4];
bytes[0] = (byte) ((ipLong >> 24) & 0xFF);
bytes[1] = (byte) ((ipLong >> 16) & 0xFF);
bytes[2] = (byte) ((ipLong >> 8) & 0xFF);
bytes[3] = (byte) (ipLong & 0xFF);
InetAddress inetAddress = InetAddress.getByAddress(bytes);
return inetAddress.getHostAddress();
} catch (UnknownHostException e) {
log.error("IP地址转换异常: " + ipLong, e);
return null;
}
}
} }

View File

@ -0,0 +1,17 @@
<?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.FormSubmissionDao">
<select id="selectByProjectIdWithFieldFilters" resultType="com.luban.api.entity.FormSubmission">
SELECT * FROM h5_form_submission
<where>
project_id = #{projectId}
<if test="fieldFilters != null and !fieldFilters.empty">
<foreach collection="fieldFilters.entrySet()" index="key" item="value">
AND JSON_UNQUOTE(JSON_EXTRACT(form_data, CONCAT('$.', #{key}))) = #{value}
</foreach>
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>