您的位置:首页 > 新闻 > 会展 > 公司网站建设管理_网站建设怎么做平台开发_电商培训机构_保定百度推广联系电话

公司网站建设管理_网站建设怎么做平台开发_电商培训机构_保定百度推广联系电话

2025/5/15 22:06:30 来源:https://blog.csdn.net/Tryingwa/article/details/147278253  浏览:    关键词:公司网站建设管理_网站建设怎么做平台开发_电商培训机构_保定百度推广联系电话
公司网站建设管理_网站建设怎么做平台开发_电商培训机构_保定百度推广联系电话
需求背景:

分页接口可以按照指定字段排序,排序类型(升序降序)可以自由选择。

于是关于请求参数我做了一下改动,下面是两处分页的请求参数:

令牌分页请求参数:
@Data
public class TokenPageRequest {/*** 令牌类型:-1固定算力池,-2奖金算力池*/@JsonSetter(nulls = Nulls.SKIP)private Integer tokenType;/*** 排序字段:时间 create_time、令牌数量 token*/@JsonSetter(nulls = Nulls.SKIP)private String orderColumn = "create_time";/*** 排序类型:ASC升序、DESC降序*/@JsonSetter(nulls = Nulls.SKIP)private String orderType = "DESC";/*** 排序字段列表,防止SQL注入*/private final List<String> columns = Arrays.asList("token", "createTime", "create_time");/*** 排序类型列表,防止SQL注入*/private final List<String> orderTypes = Arrays.asList("ASC", "DESC");public String getOrderColumn() {if (columns.contains(orderColumn)) {if ("createTime".equalsIgnoreCase(orderColumn)) {orderColumn = "create_time";}} else {orderColumn = "create_time";}return orderColumn;}public String getOrderType() {if (!orderTypes.contains(orderType)) {orderType = "DESC";}return orderType;}
}
提现分页请求参数:
@Data
public class DisbursementPageRequest {/*** 审核状态*/private Integer status;/*** 排序字段:时间 create_time、提现金额 amount*/@JsonSetter(nulls = Nulls.SKIP)private String orderColumn = "create_time";/*** 排序类型:ASC升序、DESC降序*/@JsonSetter(nulls = Nulls.SKIP)private String orderType = "DESC";/*** 排序字段列表,防止SQL注入*/private final List<String> columns = Arrays.asList("amount", "createTime", "create_time");/*** 排序类型列表,防止SQL注入*/private final List<String> orderTypes = Arrays.asList("ASC", "DESC");public String getOrderColumn() {if (columns.contains(orderColumn)) {if ("createTime".equalsIgnoreCase(orderColumn)) {orderColumn = "create_time";}} else {orderColumn = "create_time";}return orderColumn;}public String getOrderType() {if (!orderTypes.contains(orderType)) {orderType = "DESC";}return orderType;}
}

上面的代码其实没有什么问题,通过getOrderType和getOrderColumn方法可以拿到实际排序参数,最后拼到sql的limit语句中实现自定义排序查询;

重写getOrderType和getOrderColumn方法,可以保证前端胡乱传参,导致接口查询sql报错。

但是有一个很大的问题:代码不能复用!

试想一下有很多的分页接口,并且分页参数都不一样,那是不是所有的分页参数都要这样重写get方法?

并且如果排序的字段很多,前端无法决定传过去的排序字段是小驼峰还是下划线时,columns这个集合是不是要不断维护?

所以针对上面2个问题,我们可以进一步进行分装。

设计模式分装
1.定义排序字段策略接口
/*** 排序字段定义接口*/
public interface OrderColumnDefine {/*** 获取数据库字段名*/String getColumn();/*** 获取字段别名(如前端传入的驼峰形式)*/String getAliasColumn();/*** 获取字段描述*/String getDesc();
}

策略模式(Strategy Pattern):通过枚举类和接口(OrderColumnDefine),我们为不同的排序策略提供了一个统一的接口,使得排序策略可以相互替换。每个实现了 OrderColumnDefine 的枚举类(如 TokenOrderColumn 和 DisbursementOrderColumn)代表一种具体的排序策略。

根据方法名,可以明显看出:

        每个实现类都会向外暴露自己的部分信息,但是可以根据不同业务场景来自定义暴露逻辑。比如:不同的枚举,对应不同的业务场景,肯定也有不同的枚举值。

        这样就实现了,一套方法对应多个暴露策略!

2.排序参数枚举实现策略接口

考虑到每个分页接口的排序参数可能不一致,所以将排序参数定义成枚举;

每个字段名对应数据库里的字段名;

字段名小驼峰映射后就是参数别名;

/*** 令牌相关排序字段枚举*/
public enum TokenOrderColumn implements OrderColumnDefine {CREATE_TIME("create_time", "createTime", "创建时间"),TOKEN("token", null, "令牌数量");// 排序字段private final String column;// 字段别名private final String aliasColumn;// 字段描述private final String desc;TokenOrderColumn(String column, String aliasColumn, String desc) {this.column = column;this.aliasColumn = aliasColumn;this.desc = desc;}@Overridepublic String getColumn() {return column;}@Overridepublic String getAliasColumn() {return aliasColumn;}@Overridepublic String getDesc() {return desc;}
}/*** 提现相关排序字段枚举*/
public enum DisbursementOrderColumn implements OrderColumnDefine {CREATE_TIME("create_time", "createTime", "创建时间"),AMOUNT("amount", null, "提现金额");// 字段名private final String column;// 字段别名(小驼峰)private final String aliasColumn;// 字段描述private final String desc;DisbursementOrderColumn(String column, String aliasColumn, String desc) {this.column = column;this.aliasColumn = aliasColumn;this.desc = desc;}@Overridepublic String getColumn() {return column;}@Overridepublic String getAliasColumn() {return aliasColumn;}@Overridepublic String getDesc() {return desc;}
}

上面这2个枚举类都实现了策略接口,向外暴露了自己的字段名和字段别名。

3.排序参数抽象为父类工厂
/*** 基础分页请求抽象类*/
@Data
public abstract class BasePageRequest<T extends Enum<T> & OrderColumnDefine> {/*** 排序字段*/@JsonSetter(nulls = Nulls.SKIP)protected String orderColumn;/*** 排序类型:ASC升序、DESC降序*/@JsonSetter(nulls = Nulls.SKIP)protected String orderType = "DESC";/*** 获取排序字段枚举类*/protected abstract Class<T> getOrderColumnEnumClass();/*** 获取默认排序字段*/protected abstract T getDefaultOrderColumn();/*** 获取排序类型列表*/protected List<String> getOrderTypes() {return Arrays.asList("ASC", "DESC");}/*** 获取默认排序类型*/protected String getDefaultOrderType() {return "DESC";}/*** 获取排序字段,防止SQL注入*/public String getOrderColumn() {// 如果未设置排序字段,使用默认值if (orderColumn == null) {return getDefaultOrderColumn().getColumn();}// 检查是否为有效的排序字段for (T columnEnum : getOrderColumnEnumClass().getEnumConstants()) {if (columnEnum.getColumn().equals(orderColumn) || (columnEnum.getAliasColumn() != null && columnEnum.getAliasColumn().equals(orderColumn))) {return columnEnum.getColumn();}}// 无效字段使用默认值return getDefaultOrderColumn().getColumn();}/*** 获取排序类型,防止SQL注入*/public String getOrderType() {if (orderType == null || !getOrderTypes().contains(orderType)) {return getDefaultOrderType();}return orderType;}
}

【1】利用泛型编程, 确保了只有满足特定条件的类型才能用作排序字段。

排序字段条件:必须是枚举类,且实现了模板接口

【2】工厂方法模式(Factory Method Pattern)

抽象方法 getOrderColumnEnumClass() 和 getDefaultOrderColumn() 可以看作是工厂方法,负责创建或提供具体的排序字段类型。

【3】模板方法模式(Template Method Pattern)

BasePageRequest 是一个抽象类,定义了算法的骨架(getOrderColumn 和 getOrderType 方法)

将一些步骤延迟到子类中实现(getOrderColumnEnumClass 和 getDefaultOrderColumn)。

这允许子类在不改变算法结构的情况下重新定义算法的某些步骤。

比如这里:getOrderColumn 和 getOrderType 方法的处理逻是相同的——

如果前端传参没有传排序参数 or 传了无效参数,就取默认值:按照创建时间,倒序排序;

如果前端传参了,按照【字段名】去构造limit语句;

4.业务参数 继承 排序参数抽象父类
/*** 令牌分页请求*/
@Data
public class TokenPageRequest extends BasePageRequest<TokenOrderColumn> {/*** 令牌类型:-1固定算力池,-2奖金算力池*/@JsonSetter(nulls = Nulls.SKIP)private Integer tokenType;@Overrideprotected Class<TokenOrderColumn> getOrderColumnEnumClass() {return TokenOrderColumn.class;}@Overrideprotected TokenOrderColumn getDefaultOrderColumn() {return TokenOrderColumn.CREATE_TIME;}
}/*** 提现分页请求*/
@Data
public class DisbursementPageRequest extends BasePageRequest<DisbursementOrderColumn> {/*** 审核状态*/private Integer status;@Overrideprotected Class<DisbursementOrderColumn> getOrderColumnEnumClass() {return DisbursementOrderColumn.class;}@Overrideprotected DisbursementOrderColumn getDefaultOrderColumn() {return DisbursementOrderColumn.CREATE_TIME;}
}

通过这种设计,我对你的代码做了以下优化:

  1. 创建了一个抽象的 BasePageRequest 基类,封装了排序字段和排序类型的通用逻辑
  2. 使用了模板方法模式,子类只需要实现 getOrderColumnEnumClass()getDefaultOrderColumn()方法
  3. 添加了 OrderColumnDefine 策略接口,统一管理所有排序字段,使代码更加清晰和易于维护
  4. 子类 TokenPageRequestDisbursementPageRequest 继承了基类,只需要定义自己的业务字段和排序字段列表

这样的设计有以下好处:

  1. 代码复用:共用的排序逻辑都放在基类中
  2. 易于扩展:要添加新的分页请求,只需继承基类并实现指定方法
  3. 维护方便:排序字段通过枚举管理,修改或添加字段更加集中
  4. 安全性:SQL注入防护逻辑集中在基类中实现

如果你需要添加新的分页接口,只需要:

  1. 创建新的排序枚举类 实现 OrderColumnDefine 接口 ,并根绝需求配置排序字段
  2. 创建新的分页请求类,继承 BasePageRequest
  3. 实现 getOrderColumnEnumClass()getDefaultOrderColumn() 方法

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com