Compare commits
2 Commits
1ecc8fe0af
...
d0c4a6664f
| Author | SHA1 | Date | |
|---|---|---|---|
| d0c4a6664f | |||
| fb0e30ad63 |
@ -3,6 +3,8 @@ package com.zhyc.module.base.mapper;
|
||||
import com.zhyc.module.base.domain.SheepFile;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.ResultMap;
|
||||
import org.apache.ibatis.annotations.SelectProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -86,5 +88,17 @@ public interface SheepFileMapper
|
||||
@Param("sheepFile") SheepFile sheepFile
|
||||
);
|
||||
|
||||
// 修改:使用 @SelectProvider 并指定 ResultMap
|
||||
@SelectProvider(type = SheepFileSqlProvider.class, method = "selectByCondition")
|
||||
@ResultMap("SheepFileResult") // 重要:指定使用 XML 中定义的 ResultMap
|
||||
List<SheepFile> selectByCondition(
|
||||
@Param("sheepFile") SheepFile sheepFile,
|
||||
@Param("conditions") Map<String, Object> conditions
|
||||
);
|
||||
|
||||
// 修改:获取字段值的方法
|
||||
@SelectProvider(type = SheepFileSqlProvider.class, method = "selectFieldValues")
|
||||
List<String> selectFieldValuesByProvider(@Param("fieldName") String fieldName);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,182 @@
|
||||
package com.zhyc.module.base.mapper;
|
||||
|
||||
import com.zhyc.module.base.domain.SheepFile;
|
||||
import org.apache.ibatis.jdbc.SQL;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MyBatis SQL 提供者类
|
||||
*/
|
||||
public class SheepFileSqlProvider {
|
||||
|
||||
/**
|
||||
* 生成动态查询的 SQL
|
||||
*/
|
||||
public String selectByCondition(Map<String, Object> params) {
|
||||
SheepFile sheepFile = (SheepFile) params.get("sheepFile");
|
||||
Map<String, Object> conditions = (Map<String, Object>) params.get("conditions");
|
||||
|
||||
// 使用 SQL 构建器
|
||||
SQL sql = new SQL();
|
||||
|
||||
// 重要:使用与 XML 中完全相同的字段列表
|
||||
selectAllColumns(sql);
|
||||
|
||||
sql.FROM("sheep_file");
|
||||
sql.WHERE("is_delete = 0");
|
||||
|
||||
// 添加 SheepFile 条件
|
||||
addSheepFileConditions(sql, sheepFile);
|
||||
|
||||
// 添加动态条件
|
||||
addDynamicConditions(sql, conditions);
|
||||
|
||||
sql.ORDER_BY("id DESC");
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择所有列(与 XML 中的 selectSheepFileVo 保持一致)
|
||||
*/
|
||||
private void selectAllColumns(SQL sql) {
|
||||
sql.SELECT(
|
||||
"id, bs_manage_tags, ranch_id, dr_ranch, sheepfold_id, sheepfold_name, " +
|
||||
"electronic_tags, variety_id, variety, family, name, gender, birthday, " +
|
||||
"day_age, month_age, parity, birth_weight, weaning_date, status_id, " +
|
||||
"weaning_weight, current_weight, weaning_day_age, weaning_daily_gain, " +
|
||||
"breed_status_id, breed, bs_father_id, father_manage_tags, bs_mother_id, " +
|
||||
"mother_manage_tags, receptor_id, receptor_manage_tags, father_father_id, " +
|
||||
"grandfather_manage_tags, father_mother_id, grandmother_manage_tags, " +
|
||||
"father_id, maternal_grandfather_manage_tags, mother_id, " +
|
||||
"maternal_grandmother_manage_tags, mating_date, mating_type_id, " +
|
||||
"preg_date, lambing_date, lambing_day, mating_day, gestation_day, " +
|
||||
"expected_date, post_lambing_day, lactation_day, anestrous_day, " +
|
||||
"mating_counts, mating_total, miscarriage_counts, comment, " +
|
||||
"controlled, body, breast, source, source_date, source_ranch_id, " +
|
||||
"source_ranch, update_by, update_time, create_by, create_time, " +
|
||||
"is_delete"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 SheepFile 对象中的条件
|
||||
*/
|
||||
private void addSheepFileConditions(SQL sql, SheepFile sheepFile) {
|
||||
if (sheepFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(sheepFile.getBsManageTags())) {
|
||||
sql.WHERE("bs_manage_tags LIKE CONCAT('%', #{sheepFile.bsManageTags}, '%')");
|
||||
}
|
||||
if (StringUtils.hasText(sheepFile.getElectronicTags())) {
|
||||
sql.WHERE("electronic_tags LIKE CONCAT('%', #{sheepFile.electronicTags}, '%')");
|
||||
}
|
||||
if (StringUtils.hasText(sheepFile.getDrRanch())) {
|
||||
sql.WHERE("dr_ranch LIKE CONCAT('%', #{sheepFile.drRanch}, '%')");
|
||||
}
|
||||
if (StringUtils.hasText(sheepFile.getVariety())) {
|
||||
sql.WHERE("variety LIKE CONCAT('%', #{sheepFile.variety}, '%')");
|
||||
}
|
||||
if (StringUtils.hasText(sheepFile.getName())) {
|
||||
sql.WHERE("name LIKE CONCAT('%', #{sheepFile.name}, '%')");
|
||||
}
|
||||
if (sheepFile.getGender() != null) {
|
||||
sql.WHERE("gender = #{sheepFile.gender}");
|
||||
}
|
||||
if (sheepFile.getStatusId() != null) {
|
||||
sql.WHERE("status_id = #{sheepFile.statusId}");
|
||||
}
|
||||
if (StringUtils.hasText(sheepFile.getBreed())) {
|
||||
sql.WHERE("breed LIKE CONCAT('%', #{sheepFile.breed}, '%')");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的动态条件处理
|
||||
*/
|
||||
private void addDynamicConditions(SQL sql, Map<String, Object> conditions) {
|
||||
if (conditions == null || conditions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : conditions.entrySet()) {
|
||||
String field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String strValue = value.toString();
|
||||
|
||||
// 处理空值条件
|
||||
if ("IS_NULL".equals(strValue)) {
|
||||
sql.WHERE(field + " IS NULL");
|
||||
} else if ("NOT_NULL".equals(strValue)) {
|
||||
sql.WHERE(field + " IS NOT NULL");
|
||||
}
|
||||
// 处理范围条件
|
||||
else if (strValue.startsWith("GT:") || strValue.startsWith("LT:") ||
|
||||
strValue.startsWith("GE:") || strValue.startsWith("LE:")) {
|
||||
String operator = strValue.substring(0, 2);
|
||||
String numValue = strValue.substring(3);
|
||||
if (isNumeric(numValue)) {
|
||||
sql.WHERE(field + " " + operator + " " + numValue);
|
||||
}
|
||||
}
|
||||
// 处理IN条件
|
||||
else if (strValue.contains(",")) {
|
||||
String[] values = strValue.split(",");
|
||||
StringBuilder inClause = new StringBuilder(field + " IN (");
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
inClause.append("'").append(values[i].trim()).append("'");
|
||||
if (i < values.length - 1) {
|
||||
inClause.append(",");
|
||||
}
|
||||
}
|
||||
inClause.append(")");
|
||||
sql.WHERE(inClause.toString());
|
||||
}
|
||||
// 默认条件:使用等值查询
|
||||
else {
|
||||
sql.WHERE(field + " = #{conditions." + field + "}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为数字
|
||||
*/
|
||||
private boolean isNumeric(String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Double.parseDouble(str.trim());
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成获取字段值的 SQL
|
||||
*/
|
||||
public String selectFieldValues(Map<String, Object> params) {
|
||||
String fieldName = (String) params.get("fieldName");
|
||||
|
||||
SQL sql = new SQL();
|
||||
sql.SELECT("DISTINCT " + fieldName + " AS field_value");
|
||||
sql.FROM("sheep_file");
|
||||
sql.WHERE(fieldName + " IS NOT NULL");
|
||||
sql.WHERE(fieldName + " != ''");
|
||||
sql.WHERE(fieldName + " != 'null'");
|
||||
sql.ORDER_BY(fieldName + " ASC");
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
}
|
||||
@ -72,35 +72,40 @@ public class SheepFileServiceImpl implements ISheepFileService {
|
||||
|
||||
/**
|
||||
* 获取指定字段的唯一值列表
|
||||
*
|
||||
* 这个方法实现了获取字段唯一值的核心逻辑
|
||||
* 包含安全验证和业务处理
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @return 该字段的所有唯一值列表
|
||||
* @throws IllegalArgumentException 当字段名不在白名单中时抛出异常
|
||||
*/
|
||||
@Override
|
||||
public List<String> getFieldValues(String fieldName) {
|
||||
// 第一步:安全性验证 - 防止SQL注入攻击
|
||||
// 只允许预定义的字段名,确保查询的安全性
|
||||
if (!isValidFieldName(fieldName)) {
|
||||
// 如果字段名不在白名单中,抛出异常并记录日志
|
||||
throw new IllegalArgumentException("非法的字段名: " + fieldName + ",请检查字段名是否正确");
|
||||
}
|
||||
|
||||
// 第二步:调用Mapper层执行数据库查询
|
||||
// 这里会执行类似 SELECT DISTINCT fieldName FROM sheep_file 的SQL
|
||||
List<String> fieldValues = sheepFileMapper.selectFieldValues(fieldName);
|
||||
|
||||
// 第三步:返回查询结果
|
||||
return fieldValues;
|
||||
// 第二步:调用新的 Provider 方法
|
||||
// 注意:这里调用的是 selectFieldValuesByProvider,不是 selectFieldValues
|
||||
return sheepFileMapper.selectFieldValuesByProvider(fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SheepFile> selectSheepFileListByCondition(Map<String, Object> params, SheepFile sheepFile) {
|
||||
// 验证参数中的字段名,防止SQL注入
|
||||
if (params != null && !params.isEmpty()) {
|
||||
// 验证并处理参数
|
||||
Map<String, Object> safeConditions = processConditions(params);
|
||||
|
||||
// 调用新的 Provider 方法
|
||||
List<SheepFile> result = sheepFileMapper.selectByCondition(sheepFile, safeConditions);
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理条件参数,确保安全
|
||||
*/
|
||||
private Map<String, Object> processConditions(Map<String, Object> params) {
|
||||
if (params == null || params.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
Map<String, Object> safeParams = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
@ -110,20 +115,53 @@ public class SheepFileServiceImpl implements ISheepFileService {
|
||||
// 将前端字段名转换为数据库字段名
|
||||
String dbFieldName = convertToDbFieldName(fieldName);
|
||||
|
||||
// 验证字段名是否安全(使用白名单)
|
||||
// 验证字段名是否安全
|
||||
if (isValidFieldName(dbFieldName)) {
|
||||
safeParams.put(dbFieldName, value);
|
||||
// 处理值,确保不是 Character 类型
|
||||
Object safeValue = value;
|
||||
if (value != null) {
|
||||
String strValue = value.toString();
|
||||
|
||||
// 特殊处理范围条件:确保数字值的安全性
|
||||
if (strValue.startsWith("GT:") || strValue.startsWith("LT:") ||
|
||||
strValue.startsWith("GE:") || strValue.startsWith("LE:")) {
|
||||
String numPart = strValue.substring(3);
|
||||
// 验证数字部分是否安全(防止 SQL 注入)
|
||||
if (isNumeric(numPart)) {
|
||||
safeValue = strValue;
|
||||
} else {
|
||||
// 记录日志或抛出异常
|
||||
System.out.println("警告:忽略非法字段名: " + fieldName);
|
||||
// 如果不是数字,忽略这个条件
|
||||
System.out.println("警告:范围条件的值不是数字: " + fieldName + " = " + strValue);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// 其他值直接使用字符串
|
||||
safeValue = strValue;
|
||||
}
|
||||
}
|
||||
safeParams.put(dbFieldName, safeValue);
|
||||
} else {
|
||||
// 记录日志
|
||||
System.out.println("警告:忽略非法字段名: " + fieldName + " -> " + dbFieldName);
|
||||
}
|
||||
}
|
||||
|
||||
return sheepFileMapper.selectSheepFileListByCondition(safeParams, sheepFile);
|
||||
return safeParams;
|
||||
}
|
||||
|
||||
// 如果没有额外参数,使用原有的查询方法
|
||||
return sheepFileMapper.selectSheepFileList(sheepFile);
|
||||
/**
|
||||
* 判断字符串是否为数字
|
||||
*/
|
||||
private boolean isNumeric(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Double.parseDouble(str);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,10 +169,10 @@ public class SheepFileServiceImpl implements ISheepFileService {
|
||||
*/
|
||||
private String convertToDbFieldName(String fieldName) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
String result = fieldName.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
return result;
|
||||
return fieldName.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 扩展字段名白名单验证
|
||||
*/
|
||||
|
||||
@ -200,33 +200,38 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="params != null and !params.isEmpty()">
|
||||
<!-- 空值条件 -->
|
||||
<foreach collection="params.entrySet()" item="value" index="key">
|
||||
<if test="value == 'IS_NULL'">
|
||||
<if test="value != null and value.toString() == 'IS_NULL'">
|
||||
AND ${key} IS NULL
|
||||
</if>
|
||||
<if test="value == 'NOT_NULL'">
|
||||
<if test="value != null and value.toString() == 'NOT_NULL'">
|
||||
AND ${key} IS NOT NULL
|
||||
</if>
|
||||
</foreach>
|
||||
|
||||
<!-- 范围条件 -->
|
||||
<foreach collection="params.entrySet()" item="value" index="key">
|
||||
<if test="value != null and value.toString().startsWith('GT:')">
|
||||
<!-- 显式将 value 转换为字符串 -->
|
||||
<if test="value != null and value.toString() != null">
|
||||
<choose>
|
||||
<when test="value.toString().startsWith('GT:')">
|
||||
AND ${key} > #{value.toString().substring(3)}
|
||||
</if>
|
||||
<if test="value != null and value.toString().startsWith('LT:')">
|
||||
</when>
|
||||
<when test="value.toString().startsWith('LT:')">
|
||||
AND ${key} < #{value.toString().substring(3)}
|
||||
</if>
|
||||
<if test="value != null and value.toString().startsWith('GE:')">
|
||||
</when>
|
||||
<when test="value.toString().startsWith('GE:')">
|
||||
AND ${key} >= #{value.toString().substring(3)}
|
||||
</if>
|
||||
<if test="value != null and value.toString().startsWith('LE:')">
|
||||
</when>
|
||||
<when test="value.toString().startsWith('LE:')">
|
||||
AND ${key} <= #{value.toString().substring(3)}
|
||||
</when>
|
||||
</choose>
|
||||
</if>
|
||||
</foreach>
|
||||
|
||||
<!-- 列表条件 -->
|
||||
<foreach collection="params.entrySet()" item="value" index="key">
|
||||
<if test="value != null and value.toString().contains(',')">
|
||||
<if test="value != null and value.toString() != null and value.toString().contains(',')">
|
||||
AND ${key} IN
|
||||
<foreach collection="value.toString().split(',')" item="item" open="(" separator="," close=")">
|
||||
#{item}
|
||||
@ -236,35 +241,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<!-- 模糊查询条件(针对文本字段) -->
|
||||
<foreach collection="params.entrySet()" item="value" index="key">
|
||||
<if test="value != null and
|
||||
(key == 'bs_manage_tags' or key == 'electronic_tags' or
|
||||
<if test="value != null and value.toString() != null">
|
||||
<choose>
|
||||
<when test="key == 'bs_manage_tags' or key == 'electronic_tags' or
|
||||
key == 'dr_ranch' or key == 'sheepfold_name' or
|
||||
key == 'variety' or key == 'family' or
|
||||
key == 'name' or key == 'breed' or
|
||||
key == 'father_manage_tags' or key == 'mother_manage_tags' or
|
||||
key == 'receptor_manage_tags') and
|
||||
value != 'IS_NULL' and value != 'NOT_NULL' and
|
||||
!value.toString().startsWith('GT:') and !value.toString().startsWith('LT:') and
|
||||
!value.toString().startsWith('GE:') and !value.toString().startsWith('LE:') and
|
||||
!value.toString().contains(',')">
|
||||
key == 'receptor_manage_tags'">
|
||||
AND ${key} LIKE CONCAT('%', #{value}, '%')
|
||||
</if>
|
||||
</foreach>
|
||||
|
||||
</when>
|
||||
<otherwise>
|
||||
<!-- 普通等于条件 -->
|
||||
<foreach collection="params.entrySet()" item="value" index="key">
|
||||
<if test="value != null and
|
||||
value != 'IS_NULL' and value != 'NOT_NULL' and
|
||||
!value.toString().startsWith('GT:') and !value.toString().startsWith('LT:') and
|
||||
!value.toString().startsWith('GE:') and !value.toString().startsWith('LE:') and
|
||||
!value.toString().contains(',') and
|
||||
!(key == 'bs_manage_tags' or key == 'electronic_tags' or
|
||||
key == 'dr_ranch' or key == 'sheepfold_name' or
|
||||
key == 'variety' or key == 'family' or
|
||||
key == 'name' or key == 'breed' or
|
||||
key == 'father_manage_tags' or key == 'mother_manage_tags' or
|
||||
key == 'receptor_manage_tags')">
|
||||
AND ${key} = #{value}
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
</foreach>
|
||||
</if>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user