children;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiFormOptionDto.java b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiFormOptionDto.java
new file mode 100644
index 0000000..a122fde
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiFormOptionDto.java
@@ -0,0 +1,25 @@
+package apelet.common.openapi.dto;
+
+import lombok.Data;
+
+/**
+ * OpenAPI 配置页面表单下拉项。
+ *
+ * @author chenchuchuan
+ */
+@Data
+public class OpenApiFormOptionDto {
+
+ /**
+ * 在线表单主键Id。
+ */
+ private Long formId;
+ /**
+ * 表单编码。
+ */
+ private String formCode;
+ /**
+ * 表单名称。
+ */
+ private String formName;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiOperationDto.java b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiOperationDto.java
new file mode 100644
index 0000000..06fbc9c
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiOperationDto.java
@@ -0,0 +1,26 @@
+package apelet.common.openapi.dto;
+
+import lombok.Data;
+
+/**
+ * OpenAPI 可选操作下拉项(查询 + widgetJson.operationList 各项)。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+@Data
+public class OpenApiOperationDto {
+
+ /**
+ * 操作编码:query 或 operationList 中操作的 code。
+ */
+ private String code;
+ /**
+ * 操作名称:查询 或 operationList 中操作的 name。
+ */
+ private String name;
+ /**
+ * 是否启用。
+ */
+ private Boolean enabled;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiPageOptionDto.java b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiPageOptionDto.java
new file mode 100644
index 0000000..9d77768
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiPageOptionDto.java
@@ -0,0 +1,25 @@
+package apelet.common.openapi.dto;
+
+import lombok.Data;
+
+/**
+ * OpenAPI 配置页面上线页面下拉项。
+ *
+ * @author chenchuchuan
+ */
+@Data
+public class OpenApiPageOptionDto {
+
+ /**
+ * 在线页面主键Id。
+ */
+ private Long pageId;
+ /**
+ * 页面编码。
+ */
+ private String pageCode;
+ /**
+ * 页面名称。
+ */
+ private String pageName;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiTokenDto.java b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiTokenDto.java
new file mode 100644
index 0000000..4c9777e
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiTokenDto.java
@@ -0,0 +1,26 @@
+package apelet.common.openapi.dto;
+
+import lombok.Data;
+
+/**
+ * OpenAPI 按用户名换取 token 的请求体。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+@Data
+public class OpenApiTokenDto {
+
+ /**
+ * 应用编码。
+ */
+ private String appCode;
+ /**
+ * 应用密钥。
+ */
+ private String appSecret;
+ /**
+ * 调用用户名。
+ */
+ private String username;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiUserInfo.java b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiUserInfo.java
new file mode 100644
index 0000000..771ebf7
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/dto/OpenApiUserInfo.java
@@ -0,0 +1,30 @@
+package apelet.common.openapi.dto;
+
+import lombok.Data;
+
+/**
+ * OpenAPI 按用户名换取的调用用户信息(由接入方实现 OpenApiUserService 提供)。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+@Data
+public class OpenApiUserInfo {
+
+ /**
+ * 用户Id。
+ */
+ private Long userId;
+ /**
+ * 登录名。
+ */
+ private String loginName;
+ /**
+ * 显示名称。
+ */
+ private String showName;
+ /**
+ * 租户Id。
+ */
+ private Long tenantId;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/interceptor/OpenApiAuthInterceptor.java b/common/common-openapi/src/main/java/apelet/common/openapi/interceptor/OpenApiAuthInterceptor.java
new file mode 100644
index 0000000..2bd33bb
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/interceptor/OpenApiAuthInterceptor.java
@@ -0,0 +1,55 @@
+package apelet.common.openapi.interceptor;
+
+import apelet.common.core.constant.ErrorCodeEnum;
+import apelet.common.core.object.ResponseResult;
+import apelet.common.core.object.TokenData;
+import apelet.common.openapi.config.OpenApiProperties;
+import apelet.common.openapi.service.OpenApiAuthService;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * OpenAPI 对外开放路由({urlPrefix}/v1/**)的 token 校验拦截器。
+ * 在 SpringMVC 拦截器链中注册(见接入方 InterceptorConfig),只拦截 {urlPrefix}/v1/**;
+ * token 签发(/auth/token)与配置管理(/config/**、/app/**,走应用自身登录态)不经过本拦截器。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+@Slf4j
+@Component
+@ConditionalOnProperty(name = "common-openapi.operationEnabled", havingValue = "true")
+public class OpenApiAuthInterceptor implements HandlerInterceptor {
+
+ @Autowired
+ private OpenApiAuthService openApiAuthService;
+ @Autowired
+ private OpenApiProperties openApiProperties;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ // 兜底:即便注册路径放开,也仅对对外开放的动态路由做 token 校验
+ if (!request.getRequestURI().startsWith(openApiProperties.getUrlPrefix() + "/v1/")) {
+ return true;
+ }
+ String token = request.getHeader("Authorization");
+ TokenData tokenData = openApiAuthService.validateToken(token);
+ if (tokenData == null) {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ response.setContentType("application/json;charset=UTF-8");
+ response.getWriter().write(JSON.toJSONString(
+ ResponseResult.error(ErrorCodeEnum.UNAUTHORIZED_LOGIN, "OpenAPI token 无效或已过期!")));
+ return false;
+ }
+ TokenData.addToRequest(tokenData);
+ return true;
+ }
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/model/OpenApiApp.java b/common/common-openapi/src/main/java/apelet/common/openapi/model/OpenApiApp.java
new file mode 100644
index 0000000..7fc7b02
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/model/OpenApiApp.java
@@ -0,0 +1,70 @@
+package apelet.common.openapi.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * OpenAPI 第三方应用实体对象。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+@Data
+@TableName(value = "xy_sys_open_app")
+public class OpenApiApp {
+
+ /**
+ * 主键Id。
+ */
+ @TableId(value = "id")
+ private Long id;
+ /**
+ * 应用编码(对外暴露唯一)。
+ */
+ @TableField(value = "app_code")
+ private String appCode;
+ /**
+ * 应用密钥(仅后台可见,可重置)。
+ */
+ @TableField(value = "app_secret")
+ private String appSecret;
+ /**
+ * 应用名称。
+ */
+ @TableField(value = "app_name")
+ private String appName;
+ /**
+ * 归属租户Id(本期默认为空,为空表示不限租户)。
+ */
+ @TableField(value = "tenant_id")
+ private Long tenantId;
+ /**
+ * 状态(0: 停用 1: 启用)。
+ */
+ @TableField(value = "status")
+ private Integer status;
+ /**
+ * 创建时间。
+ */
+ @TableField(value = "create_time")
+ private Date createTime;
+ /**
+ * 创建者Id。
+ */
+ @TableField(value = "create_user_id")
+ private Long createUserId;
+ /**
+ * 更新时间。
+ */
+ @TableField(value = "update_time")
+ private Date updateTime;
+ /**
+ * 更新者Id。
+ */
+ @TableField(value = "update_user_id")
+ private Long updateUserId;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/model/OpenApiConfig.java b/common/common-openapi/src/main/java/apelet/common/openapi/model/OpenApiConfig.java
new file mode 100644
index 0000000..4eba81f
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/model/OpenApiConfig.java
@@ -0,0 +1,110 @@
+package apelet.common.openapi.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * OpenAPI 开放接口配置实体对象。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+@Data
+@TableName(value = "xy_sys_open_api_config")
+public class OpenApiConfig {
+
+ /**
+ * 主键Id。
+ */
+ @TableId(value = "id")
+ private Long id;
+ /**
+ * 授权开放的应用编码(关联 xy_sys_open_app.app_code)。
+ */
+ @TableField(value = "app_code")
+ private String appCode;
+ /**
+ * 在线页面主键(OnlinePage.pageId)。
+ */
+ @TableField(value = "page_id")
+ private Long pageId;
+ /**
+ * 页面编码(冗余存储,用于组装开放路径)。
+ */
+ @TableField(value = "page_code")
+ private String pageCode;
+ /**
+ * 该页面下关联的在线表单编码(OnlineForm.formCode,用于组装开放路径 URL)。
+ */
+ @TableField(value = "form_code")
+ private String formCode;
+ /**
+ * 关联的在线表单主键Id(OnlineForm.formId),执行时优先按 formId 精确定位表单。
+ */
+ @TableField(value = "form_id")
+ private Long formId;
+ /**
+ * 操作编码:query 或 operationList 中操作的 code。
+ */
+ @TableField(value = "operation_code")
+ private String operationCode;
+ /**
+ * 操作名称:查询 或 operationList 中操作的 name。
+ */
+ @TableField(value = "operation_name")
+ private String operationName;
+ /**
+ * 参数模式(后端自动推导):FIELD_DIRECT / CONDITION_MATCH / QUERY。
+ */
+ @TableField(value = "param_mode")
+ private String paramMode;
+ /**
+ * 开放路径:/openapi/v1/{pageCode}/{formCode}/{operationCode},全局唯一。
+ */
+ @TableField(value = "request_path")
+ private String requestPath;
+ /**
+ * 请求方法 GET / POST。
+ */
+ @TableField(value = "request_method")
+ private String requestMethod;
+ /**
+ * JSON:输入参数+条件配置(见设计文档 6.1)。
+ */
+ @TableField(value = "param_config")
+ private String paramConfig;
+ /**
+ * JSON:输出层级结构(见设计文档 6.2)。
+ */
+ @TableField(value = "output_config")
+ private String outputConfig;
+ /**
+ * 状态(0: 停用 1: 启用)。
+ */
+ @TableField(value = "status")
+ private Integer status;
+ /**
+ * 创建时间。
+ */
+ @TableField(value = "create_time")
+ private Date createTime;
+ /**
+ * 创建者Id。
+ */
+ @TableField(value = "create_user_id")
+ private Long createUserId;
+ /**
+ * 更新时间。
+ */
+ @TableField(value = "update_time")
+ private Date updateTime;
+ /**
+ * 更新者Id。
+ */
+ @TableField(value = "update_user_id")
+ private Long updateUserId;
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiAppService.java b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiAppService.java
new file mode 100644
index 0000000..dca490f
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiAppService.java
@@ -0,0 +1,40 @@
+package apelet.common.openapi.service;
+
+import apelet.common.core.base.service.IBaseService;
+import apelet.common.openapi.model.OpenApiApp;
+
+import java.util.List;
+
+/**
+ * OpenAPI 第三方应用数据操作服务接口。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+public interface OpenApiAppService extends IBaseService {
+
+ /**
+ * 获取过滤后的应用列表。
+ *
+ * @param filter 过滤对象。
+ * @param orderBy 排序字符串。
+ * @return 应用列表。
+ */
+ List getOpenApiAppList(OpenApiApp filter, String orderBy);
+
+ /**
+ * 新增应用:appCode 为空时自动生成,appSecret 自动生成并返回明文。
+ *
+ * @param app 应用对象。
+ * @return 保存后的应用(含明文 appSecret)。
+ */
+ OpenApiApp addNew(OpenApiApp app);
+
+ /**
+ * 重置应用密钥:旧密钥立即失效。
+ *
+ * @param id 应用主键Id。
+ * @return 新的明文 appSecret。
+ */
+ String resetSecret(Long id);
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiAuthService.java b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiAuthService.java
new file mode 100644
index 0000000..b2890d7
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiAuthService.java
@@ -0,0 +1,32 @@
+package apelet.common.openapi.service;
+
+import apelet.common.core.object.ResponseResult;
+import apelet.common.core.object.TokenData;
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * OpenAPI 鉴权服务接口:token 签发与校验。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+public interface OpenApiAuthService {
+
+ /**
+ * 校验应用凭证 + 调用用户名,签发 token。
+ *
+ * @param appCode 应用编码。
+ * @param appSecret 应用密钥。
+ * @param username 调用用户名。
+ * @return data 含 accessToken(不含 Bearer 前缀)与 expiresIn。
+ */
+ ResponseResult issueToken(String appCode, String appSecret, String username);
+
+ /**
+ * 校验 Bearer token:验签 + 有效期 + Redis 会话存在性,还原 TokenData。
+ *
+ * @param token Authorization 头值(含 Bearer 前缀)。
+ * @return TokenData,无效或已失效返回null。
+ */
+ TokenData validateToken(String token);
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiConfigService.java b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiConfigService.java
new file mode 100644
index 0000000..c8977da
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiConfigService.java
@@ -0,0 +1,84 @@
+package apelet.common.openapi.service;
+
+import apelet.common.core.base.service.IBaseService;
+import apelet.common.openapi.dto.OpenApiFieldDto;
+import apelet.common.openapi.dto.OpenApiFormOptionDto;
+import apelet.common.openapi.dto.OpenApiOperationDto;
+import apelet.common.openapi.dto.OpenApiPageOptionDto;
+import apelet.common.openapi.model.OpenApiConfig;
+
+import java.util.List;
+
+/**
+ * OpenAPI 开放接口配置数据操作服务接口。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+public interface OpenApiConfigService extends IBaseService {
+
+ /**
+ * 获取过滤后的配置列表。
+ *
+ * @param filter 过滤对象。
+ * @param orderBy 排序字符串。
+ * @return 配置列表。
+ */
+ List getOpenApiConfigList(OpenApiConfig filter, String orderBy);
+
+ /**
+ * 获取在线页面下拉列表(配置步骤1),可按页面名称模糊搜索。
+ *
+ * @param keyword 页面名称关键字,为空返回全部。
+ * @return 页面列表。
+ */
+ List getPageOptionList(String keyword);
+
+ /**
+ * 获取指定页面关联的表单下拉列表(配置步骤2 联动)。
+ *
+ * @param pageId 页面主键Id。
+ * @return 表单列表。
+ */
+ List getFormOptionList(Long pageId);
+
+ /**
+ * 获取可选操作下拉:查询(query) + widgetJson.operationList 各项(配置步骤3)。
+ *
+ * @param formId 在线表单主键Id。
+ * @return 操作列表。
+ */
+ List getOperationList(Long formId);
+
+ /**
+ * 获取表单可配置字段树:主表字段 + 关联字段属性 + 一对多分录附表字段。
+ *
+ * @param formId 在线表单主键Id。
+ * @return 字段树。
+ */
+ List getFieldTree(Long formId);
+
+ /**
+ * 新增配置:后端推导 paramMode、组装 requestPath。
+ *
+ * @param config 配置对象。
+ * @return 保存后的配置。
+ */
+ OpenApiConfig addNew(OpenApiConfig config);
+
+ /**
+ * 更新配置:重新推导 paramMode、组装 requestPath。
+ *
+ * @param config 配置对象。
+ * @return 更新后的配置,不存在返回null。
+ */
+ OpenApiConfig updateConfig(OpenApiConfig config);
+
+ /**
+ * 按开放路径查找启用的配置(动态路由执行用)。
+ *
+ * @param requestPath 开放路径。
+ * @return 配置对象。
+ */
+ OpenApiConfig getByRequestPath(String requestPath);
+}
diff --git a/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiExecService.java b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiExecService.java
new file mode 100644
index 0000000..e6a49f8
--- /dev/null
+++ b/common/common-openapi/src/main/java/apelet/common/openapi/service/OpenApiExecService.java
@@ -0,0 +1,23 @@
+package apelet.common.openapi.service;
+
+import apelet.common.core.object.ResponseResult;
+
+import java.util.Map;
+
+/**
+ * OpenAPI 开放接口执行服务接口:按配置定位表单并执行。
+ *
+ * @author chenchuchuan
+ * @date 2026-09-02
+ */
+public interface OpenApiExecService {
+
+ /**
+ * 按开放路径执行开放接口(鉴权由拦截器完成,本方法假定已注入 TokenData)。
+ *
+ * @param requestPath 开放路径(/openapi/v1/{pageCode}/{formCode}/{operationCode})。
+ * @param requestParams 请求体参数(扁平 JSON,含 pageNo/pageSize 或字段直传/条件参数)。
+ * @return 执行结果。
+ */
+ ResponseResult