diff --git a/continew-admin-open/pom.xml b/continew-admin-open/pom.xml
new file mode 100644
index 00000000..a1724ac0
--- /dev/null
+++ b/continew-admin-open/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ top.continew
+ continew-admin
+ ${revision}
+
+
+ continew-admin-open
+ 能力开放模块(包括应用管理、API开放授权、API开发等)
+
+
+
+
+ top.continew
+ continew-admin-common
+
+
+
+
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/handler/SaCheckPermissionHandler.java b/continew-admin-open/src/main/java/top/continew/admin/open/handler/SaCheckPermissionHandler.java
new file mode 100644
index 00000000..3a311119
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/handler/SaCheckPermissionHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.handler;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.handler.SaAnnotationHandlerInterface;
+import org.springframework.stereotype.Component;
+import top.continew.admin.open.util.ApiSignCheckUtils;
+
+import java.lang.reflect.Method;
+
+import static cn.dev33.satoken.annotation.handler.SaCheckPermissionHandler._checkMethod;
+
+/**
+ * 重定义注解 SaCheckPermission 的处理器
+ *
+ * @author chengzi
+ * @since 2024/10/25 12:03
+ */
+@Component
+public class SaCheckPermissionHandler implements SaAnnotationHandlerInterface {
+
+ @Override
+ public Class getHandlerAnnotationClass() {
+ return SaCheckPermission.class;
+ }
+
+ @Override
+ public void checkMethod(SaCheckPermission at, Method method) {
+ if (!ApiSignCheckUtils.isExistSignParam()) {
+ _checkMethod(at.type(), at.value(), at.mode(), at.orRole());
+ }
+ }
+
+}
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/mapper/AppMapper.java b/continew-admin-open/src/main/java/top/continew/admin/open/mapper/AppMapper.java
new file mode 100644
index 00000000..121ac571
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/mapper/AppMapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.mapper;
+
+import top.continew.starter.data.mp.base.BaseMapper;
+import top.continew.admin.open.model.entity.AppDO;
+
+/**
+ * 应用 Mapper
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+public interface AppMapper extends BaseMapper {}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/model/entity/AppDO.java b/continew-admin-open/src/main/java/top/continew/admin/open/model/entity/AppDO.java
new file mode 100644
index 00000000..6e11eca5
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/model/entity/AppDO.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.model.entity;
+
+import java.io.Serial;
+import java.time.*;
+
+import lombok.Data;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import top.continew.starter.extension.crud.model.entity.BaseDO;
+
+/**
+ * 应用实体
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Data
+@TableName("sys_app")
+public class AppDO extends BaseDO {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * ID
+ */
+ private Long id;
+
+ /**
+ * 应用名称
+ */
+ private String name;
+
+ /**
+ * APPKEY
+ */
+ private String appKey;
+
+ /**
+ * APPSECRET
+ */
+ private String appSecret;
+
+ /**
+ * 应用状态
+ */
+ private String status;
+
+ /**
+ * 失效时间
+ */
+ private LocalDateTime expirationTime;
+
+ /**
+ * 应用描述
+ */
+ private String appDesc;
+
+ /**
+ * secret查看状态
+ */
+ private String secretStatus;
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/model/query/AppQuery.java b/continew-admin-open/src/main/java/top/continew/admin/open/model/query/AppQuery.java
new file mode 100644
index 00000000..0c843751
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/model/query/AppQuery.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.model.query;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+import lombok.Data;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import top.continew.starter.data.core.annotation.Query;
+import top.continew.starter.data.core.enums.QueryType;
+
+/**
+ * 应用查询条件
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Data
+@Schema(description = "应用查询条件")
+public class AppQuery implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 应用名称
+ */
+ @Schema(description = "应用名称")
+ @Query(type = QueryType.LIKE)
+ private String name;
+
+ /**
+ * APPKEY
+ */
+ @Schema(description = "APPKEY")
+ @Query(type = QueryType.LIKE)
+ private String appKey;
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/model/req/AppReq.java b/continew-admin-open/src/main/java/top/continew/admin/open/model/req/AppReq.java
new file mode 100644
index 00000000..de21a2b6
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/model/req/AppReq.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.model.req;
+
+import java.io.Serial;
+import java.time.*;
+
+import jakarta.validation.constraints.*;
+
+import lombok.Data;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import org.hibernate.validator.constraints.Length;
+
+import top.continew.starter.extension.crud.model.req.BaseReq;
+
+/**
+ * 创建或修改应用信息
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Data
+@Schema(description = "创建或修改应用信息")
+public class AppReq extends BaseReq {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 应用名称
+ */
+ @Schema(description = "应用名称")
+ @NotBlank(message = "应用名称不能为空")
+ @Length(max = 255, message = "应用名称长度不能超过 {max} 个字符")
+ private String name;
+
+ /**
+ * APPKEY
+ */
+ @Schema(description = "应用密钥")
+ @NotBlank(message = "应用密钥不能为空")
+ @Length(max = 255, message = "应用密钥长度不能超过 {max} 个字符")
+ private String appKey;
+
+ /**
+ * 应用状态
+ */
+ @Schema(description = "应用状态")
+ @NotBlank(message = "应用状态不能为空")
+ @Length(max = 255, message = "应用状态长度不能超过 {max} 个字符")
+ private String status;
+
+ /**
+ * 失效时间
+ */
+ @Schema(description = "失效时间")
+ @NotNull(message = "失效时间不能为空")
+ private LocalDateTime expirationTime;
+
+ /**
+ * 应用描述
+ */
+ @Schema(description = "应用描述")
+ @Length(max = 255, message = "应用描述长度不能超过 {max} 个字符")
+ private String appDesc;
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppDetailResp.java b/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppDetailResp.java
new file mode 100644
index 00000000..7a3342d8
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppDetailResp.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.model.resp;
+
+import java.io.Serial;
+import java.time.*;
+
+import lombok.Data;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+
+import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
+
+/**
+ * 应用详情信息
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Data
+@ExcelIgnoreUnannotated
+@Schema(description = "应用详情信息")
+public class AppDetailResp extends BaseDetailResp {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 应用名称
+ */
+ @Schema(description = "应用名称")
+ @ExcelProperty(value = "应用名称")
+ private String name;
+
+ /**
+ * 应用密钥
+ */
+ @Schema(description = "应用密钥")
+ @ExcelProperty(value = "应用密钥")
+ private String appKey;
+
+ /**
+ * 应用状态
+ */
+ @Schema(description = "应用状态")
+ @ExcelProperty(value = "应用状态")
+ private String status;
+
+ /**
+ * 失效时间
+ */
+ @Schema(description = "失效时间")
+ @ExcelProperty(value = "失效时间")
+ private LocalDateTime expirationTime;
+
+ /**
+ * 应用描述
+ */
+ @Schema(description = "应用描述")
+ @ExcelProperty(value = "应用描述")
+ private String appDesc;
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppResp.java b/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppResp.java
new file mode 100644
index 00000000..8457ef90
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppResp.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.model.resp;
+
+import java.io.Serial;
+import java.time.*;
+
+import lombok.Data;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import top.continew.starter.extension.crud.model.resp.BaseResp;
+
+/**
+ * 应用信息
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Data
+@Schema(description = "应用信息")
+public class AppResp extends BaseResp {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 应用名称
+ */
+ @Schema(description = "应用名称")
+ private String name;
+
+ /**
+ * APPKEY
+ */
+ @Schema(description = "应用密钥")
+ private String appKey;
+
+ /**
+ * 应用状态
+ */
+ @Schema(description = "应用状态")
+ private String status;
+
+ /**
+ * 失效时间
+ */
+ @Schema(description = "失效时间")
+ private LocalDateTime expirationTime;
+
+ /**
+ * 应用描述
+ */
+ @Schema(description = "应用描述")
+ private String appDesc;
+
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppSecretGetResp.java b/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppSecretGetResp.java
new file mode 100644
index 00000000..cb04f07d
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/model/resp/AppSecretGetResp.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.model.resp;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+
+/**
+ * 应用密钥/密码信息
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Data
+@Schema(description = "应用密钥/密码信息")
+public class AppSecretGetResp {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 应用密钥
+ */
+ @Schema(description = "应用密钥")
+ private String appKey;
+
+ /**
+ * 应用密码
+ */
+ @Schema(description = "应用密码")
+ private String appSecret;
+}
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/service/AppService.java b/continew-admin-open/src/main/java/top/continew/admin/open/service/AppService.java
new file mode 100644
index 00000000..7ef07137
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/service/AppService.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.service;
+
+import top.continew.admin.open.model.resp.AppSecretGetResp;
+import top.continew.starter.extension.crud.service.BaseService;
+import top.continew.admin.open.model.query.AppQuery;
+import top.continew.admin.open.model.req.AppReq;
+import top.continew.admin.open.model.resp.AppDetailResp;
+import top.continew.admin.open.model.resp.AppResp;
+
+/**
+ * 应用业务接口
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+public interface AppService extends BaseService {
+ /**
+ * 根据ID查询应用密码
+ *
+ * @param id ID
+ * @return 应用密码
+ */
+ AppSecretGetResp getAppSecretById(Long id);
+
+ /**
+ * 根据ID重置应用密码查看状态
+ *
+ * @param id ID
+ */
+ void resetAppSecretStatusById(Long id, String status);
+
+ /**
+ * 根据应用密钥重置应用密码查看状态
+ *
+ * @param appKey 应用密钥
+ */
+ void resetAppSecretStatusByAppkey(String appKey, String status);
+
+ /**
+ * 根据ID刷新应用密码
+ *
+ * @param id ID
+ */
+ void refreshAppSecretByID(Long id);
+
+ /**
+ * 根据应用密钥获取应用密码
+ *
+ * @param appKey 应用密钥
+ * @return 应用密码
+ */
+ String getAppSecretByAppKey(String appKey);
+
+ /**
+ * 判断应用密钥是否存在
+ *
+ * @param appKey 应用密钥
+ * @return 是否存在(true:存在;false:不存在)
+ */
+ boolean isExistAppKey(String appKey);
+
+ /**
+ * 判断应用密钥是否过期
+ *
+ * @param appKey 应用密钥
+ * @return 是否过期(true:已过期;false:未过期)
+ */
+ boolean isExpireAppKey(String appKey);
+
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/service/impl/AppServiceImpl.java b/continew-admin-open/src/main/java/top/continew/admin/open/service/impl/AppServiceImpl.java
new file mode 100644
index 00000000..1a25b16e
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/service/impl/AppServiceImpl.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.IdUtil;
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.stereotype.Service;
+
+import top.continew.admin.open.model.resp.AppSecretGetResp;
+import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
+import top.continew.admin.open.mapper.AppMapper;
+import top.continew.admin.open.model.entity.AppDO;
+import top.continew.admin.open.model.query.AppQuery;
+import top.continew.admin.open.model.req.AppReq;
+import top.continew.admin.open.model.resp.AppDetailResp;
+import top.continew.admin.open.model.resp.AppResp;
+import top.continew.admin.open.service.AppService;
+
+import java.time.LocalDateTime;
+
+/**
+ * 应用业务实现
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Service
+@RequiredArgsConstructor
+public class AppServiceImpl extends BaseServiceImpl implements AppService {
+
+ // 已激活
+ private final static String APP_ENABLED_KEY = "1";
+ // 未激活
+ private final static String APP_DISABLED_KEY = "0";
+
+ @Override
+ public AppSecretGetResp getAppSecretById(Long id) {
+ AppDO app = baseMapper.lambdaQuery().eq(AppDO::getId, id).one();
+ String appSecret = "********";
+ if (app.getSecretStatus().equals(APP_DISABLED_KEY)) {
+ appSecret = app.getAppSecret();
+ this.resetAppSecretStatusById(id, APP_ENABLED_KEY);
+ }
+ AppSecretGetResp appSecretGetResp = new AppSecretGetResp();
+ appSecretGetResp.setAppKey(app.getAppKey());
+ appSecretGetResp.setAppSecret(appSecret);
+ return appSecretGetResp;
+ }
+
+ @Override
+ public void resetAppSecretStatusById(Long id, String status) {
+ baseMapper.lambdaUpdate().set(AppDO::getSecretStatus, status).eq(AppDO::getId, id).update();
+ }
+
+ @Override
+ public void resetAppSecretStatusByAppkey(String appKey, String status) {
+ baseMapper.lambdaUpdate().set(AppDO::getSecretStatus, status).eq(AppDO::getAppKey, appKey).update();
+ }
+
+ @Override
+ public void refreshAppSecretByID(Long id) {
+ baseMapper.lambdaUpdate().set(AppDO::getAppSecret, IdUtil.simpleUUID()).eq(AppDO::getId, id).update();
+ this.resetAppSecretStatusById(id, APP_DISABLED_KEY);
+ }
+
+ @Override
+ public String getAppSecretByAppKey(String appKey) {
+ return baseMapper.lambdaQuery().select(AppDO::getAppSecret).eq(AppDO::getAppKey, appKey).one().getAppSecret();
+ }
+
+ @Override
+ public boolean isExistAppKey(String appKey) {
+ return baseMapper.lambdaQuery().eq(AppDO::getAppKey, appKey).exists();
+ }
+
+ @Override
+ public boolean isExpireAppKey(String appKey) {
+ LocalDateTime expirationTime = baseMapper.lambdaQuery()
+ .select(AppDO::getExpirationTime)
+ .eq(AppDO::getAppKey, appKey)
+ .one()
+ .getExpirationTime();
+ return expirationTime.isBefore(LocalDateTimeUtil.of(DateUtil.date()));
+ }
+}
\ No newline at end of file
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/sign/OpenSignTemplate.java b/continew-admin-open/src/main/java/top/continew/admin/open/sign/OpenSignTemplate.java
new file mode 100644
index 00000000..ad6984dd
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/sign/OpenSignTemplate.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.sign;
+
+import cn.dev33.satoken.error.SaErrorCode;
+import cn.dev33.satoken.exception.SaSignException;
+import cn.dev33.satoken.sign.SaSignTemplate;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import top.continew.admin.open.service.AppService;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import static cn.dev33.satoken.SaManager.log;
+
+@Component
+@RequiredArgsConstructor
+public class OpenSignTemplate extends SaSignTemplate {
+
+ private final AppService appService;
+ public static String appKey = "appkey";
+
+ @Override
+ public void checkParamMap(Map paramMap) {
+ // 获取必须的参数
+ String timestampValue = paramMap.get(timestamp);
+ String nonceValue = paramMap.get(nonce);
+ String signValue = paramMap.get(sign);
+ String appKeyValue = paramMap.get(appKey);
+
+ // 参数非空校验
+ SaSignException.notEmpty(timestampValue, "缺少 timestamp 字段");
+ SaSignException.notEmpty(nonceValue, "缺少 nonce 字段");
+ SaSignException.notEmpty(signValue, "缺少 sign 字段");
+ SaSignException.notEmpty(appKeyValue, "缺少 appkey 字段");
+
+ // 应用存在性校验
+ SaSignException.notTrue(!appService.isExistAppKey(appKeyValue), "应用不存在");
+
+ // 应用是否过期校验
+ SaSignException.notTrue(appService.isExpireAppKey(appKeyValue), "应用已过期");
+
+ // 依次校验三个参数
+ checkTimestamp(Long.parseLong(timestampValue));
+ checkNonce(nonceValue);
+ checkSign(paramMap, signValue);
+
+ // 通过 √
+ }
+
+ @Override
+ public String createSign(Map paramsMap) {
+ // 根据应用密钥获取对应的应用密码
+ String appKey = (String)((Map)paramsMap).get("appkey");
+ String secretKey = this.appService.getAppSecretByAppKey(appKey);
+ SaSignException.notEmpty(secretKey, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201);
+
+ // 如果调用者不小心传入了 sign 参数,则此处需要将 sign 参数排除在外
+ if (paramsMap.containsKey(sign)) {
+ // 为了保证不影响原有的 paramsMap,此处需要再复制一份
+ paramsMap = new TreeMap<>(paramsMap);
+ paramsMap.remove(sign);
+ }
+
+ // 计算签名
+ String paramsStr = joinParamsDictSort(paramsMap);
+ String fullStr = paramsStr + "&" + key + "=" + secretKey;
+ String signStr = abstractStr(fullStr);
+
+ // 输入日志,方便调试
+ log.debug("fullStr:{}", fullStr);
+ log.debug("signStr:{}", signStr);
+
+ // 返回
+ return signStr;
+ }
+}
diff --git a/continew-admin-open/src/main/java/top/continew/admin/open/util/ApiSignCheckUtils.java b/continew-admin-open/src/main/java/top/continew/admin/open/util/ApiSignCheckUtils.java
new file mode 100644
index 00000000..0f43e2fa
--- /dev/null
+++ b/continew-admin-open/src/main/java/top/continew/admin/open/util/ApiSignCheckUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.open.util;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.context.model.SaRequest;
+
+import java.util.List;
+
+/**
+ * API签名验证工具类
+ *
+ * @author chengzi
+ * @since 2024/10/25 15:31
+ */
+public class ApiSignCheckUtils {
+
+ private ApiSignCheckUtils() {
+ }
+
+ /**
+ * 判断请求是否包含sign参数
+ *
+ * @return 是否包含sign参数(true:包含;false:不包含)
+ */
+ public static boolean isExistSignParam() {
+ SaRequest saRequest = SaHolder.getRequest();
+ List paramNames = saRequest.getParamNames();
+ return paramNames.stream().anyMatch(paramName -> paramName.equals("sign"));
+ }
+
+}
diff --git a/continew-admin-webapi/pom.xml b/continew-admin-webapi/pom.xml
index a0473bd8..01b837db 100644
--- a/continew-admin-webapi/pom.xml
+++ b/continew-admin-webapi/pom.xml
@@ -50,6 +50,12 @@
continew-admin-generator
+
+
+ top.continew
+ continew-admin-open
+
+
org.liquibase
diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java
index 71ac153b..2695e109 100644
--- a/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java
+++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java
@@ -16,8 +16,12 @@
package top.continew.admin.config.satoken;
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.sign.SaSignUtil;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import lombok.RequiredArgsConstructor;
@@ -25,10 +29,13 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
+import top.continew.admin.open.sign.OpenSignTemplate;
import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.validate.CheckUtils;
+import java.util.List;
+
/**
* Sa-Token 配置
*
@@ -41,6 +48,7 @@ public class SaTokenConfiguration {
private final SaTokenExtensionProperties properties;
private final LoginPasswordProperties loginPasswordProperties;
+ private final OpenSignTemplate openSignTemplate;
/**
* Sa-Token 权限认证配置
@@ -55,15 +63,31 @@ public class SaTokenConfiguration {
*/
@Bean
public SaInterceptor saInterceptor() {
+ SaManager.setSaSignTemplate(openSignTemplate);
return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN)
.notMatch(properties.getSecurity().getExcludes())
.check(r -> {
- StpUtil.checkLogin();
- if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) {
- return;
+ // 拦截验证sign
+ // 判断是否包含sign参数
+ SaRequest saRequest = SaHolder.getRequest();
+ List paramNames = saRequest.getParamNames();
+ boolean matchParamSign = paramNames.stream().anyMatch(paramName -> paramName.equals("sign"));
+ // 如果包含sign参数走SaToken API接口参数签名验证
+ if (matchParamSign) {
+ try {
+ SaSignUtil.checkRequest(saRequest);
+ } catch (Exception e) {
+ CheckUtils.throwIf(true, e.getMessage());
+ }
+ } else {
+ // 如果不包含sign参数走登录token验证
+ StpUtil.checkLogin();
+ if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) {
+ return;
+ }
+ UserContext userContext = UserContextHolder.getContext();
+ CheckUtils.throwIf(userContext.isPasswordExpired(), "密码已过期,请修改密码");
}
- UserContext userContext = UserContextHolder.getContext();
- CheckUtils.throwIf(userContext.isPasswordExpired(), "密码已过期,请修改密码");
}));
}
}
diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/open/AppController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/open/AppController.java
new file mode 100644
index 00000000..de858d97
--- /dev/null
+++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/open/AppController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.admin.controller.open;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import lombok.RequiredArgsConstructor;
+import top.continew.admin.open.model.resp.AppSecretGetResp;
+import top.continew.starter.extension.crud.enums.Api;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import org.springframework.web.bind.annotation.*;
+
+import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
+import top.continew.starter.extension.crud.controller.BaseController;
+import top.continew.admin.open.model.query.AppQuery;
+import top.continew.admin.open.model.req.AppReq;
+import top.continew.admin.open.model.resp.AppDetailResp;
+import top.continew.admin.open.model.resp.AppResp;
+import top.continew.admin.open.service.AppService;
+import top.continew.starter.extension.crud.model.resp.BaseIdResp;
+
+/**
+ * 应用管理 API
+ *
+ * @author chengzi
+ * @since 2024/10/17 16:03
+ */
+@Tag(name = "应用管理 API")
+@RestController
+@RequiredArgsConstructor
+@CrudRequestMapping(value = "/open/app", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
+public class AppController extends BaseController {
+
+ private final AppService appService;
+ private final static String APP_DISABLED_KEY = "0";
+
+ @Operation(summary = "刷新应用密码", description = "刷新应用密码")
+ @Parameter(name = "id", description = "ID", example = "test", in = ParameterIn.PATH)
+ @SaCheckPermission("open:app:refreshas")
+ @GetMapping(value = "/{id}/refreshas")
+ public void refreshAppSecret(@PathVariable Long id) {
+ appService.refreshAppSecretByID(id);
+ }
+
+ @Operation(summary = "获取应用密码", description = "获取应用密码")
+ @Parameter(name = "appKey", description = "应用密钥", example = "test", in = ParameterIn.PATH)
+ @SaCheckPermission("open:app:getas")
+ @GetMapping("/{id}/appsecret")
+ public AppSecretGetResp getAppSecret(@PathVariable Long id) {
+ return appService.getAppSecretById(id);
+ }
+
+ @Override
+ public BaseIdResp add(AppReq req) {
+ BaseIdResp baseIdResp = super.add(req);
+ Long appId = baseIdResp.getId();
+ appService.refreshAppSecretByID(appId);
+ appService.resetAppSecretStatusById(appId, APP_DISABLED_KEY);
+ return baseIdResp;
+ }
+}
\ No newline at end of file
diff --git a/continew-admin-webapi/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java b/continew-admin-webapi/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java
new file mode 100644
index 00000000..21f31513
--- /dev/null
+++ b/continew-admin-webapi/src/main/java/top/continew/starter/extension/crud/controller/BaseController.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.starter.extension.crud.controller;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import top.continew.admin.open.util.ApiSignCheckUtils;
+import top.continew.starter.core.constant.StringConstants;
+import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
+import top.continew.starter.extension.crud.enums.Api;
+import top.continew.starter.extension.crud.model.query.PageQuery;
+import top.continew.starter.extension.crud.model.query.SortQuery;
+import top.continew.starter.extension.crud.model.req.BaseReq;
+import top.continew.starter.extension.crud.model.resp.BaseIdResp;
+import top.continew.starter.extension.crud.model.resp.PageResp;
+import top.continew.starter.extension.crud.service.BaseService;
+import top.continew.starter.extension.crud.util.ValidateGroup;
+
+import java.util.List;
+
+/**
+ * 控制器基类
+ *
+ * @param 业务接口
+ * @param 列表类型
+ * @param 详情类型
+ * @param 查询条件
+ * @param 创建或修改类型
+ * @author Charles7c
+ * @since 1.0.0
+ */
+public abstract class BaseController, L, D, Q, C extends BaseReq> {
+
+ @Autowired
+ protected S baseService;
+
+ /**
+ * 分页查询列表
+ *
+ * @param query 查询条件
+ * @param pageQuery 分页查询条件
+ * @return 分页信息
+ */
+ @Operation(summary = "分页查询列表", description = "分页查询列表")
+ @ResponseBody
+ @GetMapping
+ public PageResp page(Q query, @Validated PageQuery pageQuery) {
+ this.checkPermission(Api.LIST);
+ return baseService.page(query, pageQuery);
+ }
+
+ /**
+ * 查询树列表
+ *
+ * @param query 查询条件
+ * @param sortQuery 排序查询条件
+ * @return 树列表信息
+ */
+ @Operation(summary = "查询树列表", description = "查询树列表")
+ @ResponseBody
+ @GetMapping("/tree")
+ public List> tree(Q query, SortQuery sortQuery) {
+ this.checkPermission(Api.LIST);
+ return baseService.tree(query, sortQuery, false);
+ }
+
+ /**
+ * 查询列表
+ *
+ * @param query 查询条件
+ * @param sortQuery 排序查询条件
+ * @return 列表信息
+ */
+ @Operation(summary = "查询列表", description = "查询列表")
+ @ResponseBody
+ @GetMapping("/list")
+ public List list(Q query, SortQuery sortQuery) {
+ this.checkPermission(Api.LIST);
+ return baseService.list(query, sortQuery);
+ }
+
+ /**
+ * 查询详情
+ *
+ * @param id ID
+ * @return 详情信息
+ */
+ @Operation(summary = "查询详情", description = "查询详情")
+ @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
+ @ResponseBody
+ @GetMapping("/{id}")
+ public D get(@PathVariable("id") Long id) {
+ this.checkPermission(Api.LIST);
+ return baseService.get(id);
+ }
+
+ /**
+ * 新增
+ *
+ * @param req 创建信息
+ * @return 自增 ID
+ */
+ @Operation(summary = "新增数据", description = "新增数据")
+ @ResponseBody
+ @PostMapping
+ public BaseIdResp add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) {
+ this.checkPermission(Api.ADD);
+ return BaseIdResp.builder().id(baseService.add(req)).build();
+ }
+
+ /**
+ * 修改
+ *
+ * @param req 修改信息
+ * @param id ID
+ */
+ @Operation(summary = "修改数据", description = "修改数据")
+ @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
+ @ResponseBody
+ @PutMapping("/{id}")
+ public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) {
+ this.checkPermission(Api.UPDATE);
+ baseService.update(req, id);
+ }
+
+ /**
+ * 删除
+ *
+ * @param ids ID 列表
+ */
+ @Operation(summary = "删除数据", description = "删除数据")
+ @Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
+ @ResponseBody
+ @DeleteMapping("/{ids}")
+ public void delete(@PathVariable("ids") List ids) {
+ this.checkPermission(Api.DELETE);
+ baseService.delete(ids);
+ }
+
+ /**
+ * 导出
+ *
+ * @param query 查询条件
+ * @param sortQuery 排序查询条件
+ * @param response 响应对象
+ */
+ @ExcludeFromGracefulResponse
+ @Operation(summary = "导出数据", description = "导出数据")
+ @GetMapping("/export")
+ public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
+ this.checkPermission(Api.EXPORT);
+ baseService.export(query, sortQuery, response);
+ }
+
+ /**
+ * 根据 API 类型进行权限验证
+ *
+ * @param api API 类型
+ */
+ protected void checkPermission(Api api) {
+ // 判断是否包含sign参数
+ if (!ApiSignCheckUtils.isExistSignParam()) {
+ CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
+ String path = crudRequestMapping.value();
+ String permissionPrefix = String.join(StringConstants.COLON, CharSequenceUtil
+ .splitTrim(path, StringConstants.SLASH));
+ StpUtil.checkPermission("%s:%s".formatted(permissionPrefix, api.name().toLowerCase()));
+ }
+ }
+}
diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_change_v3.4.0.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_change_v3.4.0.sql
index 96903ff4..4a991bc2 100644
--- a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_change_v3.4.0.sql
+++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_change_v3.4.0.sql
@@ -4,3 +4,34 @@ ALTER TABLE sys_notice
ADD COLUMN notice_scope INT NOT NULL COMMENT '通知范围' AFTER terminate_time,
ADD COLUMN notice_users JSON DEFAULT NULL COMMENT '通知用户' AFTER notice_scope;
COMMIT;
+
+-- changeset chengzi
+-- comment 新增应用表
+CREATE TABLE IF NOT EXISTS `sys_app` (
+ `id` bigint(0) NOT NULL COMMENT 'ID',
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用名称',
+ `app_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用密钥',
+ `app_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用密码',
+ `status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用状态(0:未激活;1:激活)',
+ `expiration_time` datetime(0) NULL DEFAULT NULL COMMENT '失效时间',
+ `app_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用描述',
+ `secret_status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用密码查看状态(0:未查看;1:已查看)',
+ `create_user` bigint(0) NOT NULL COMMENT '创建人',
+ `create_time` datetime(0) NOT NULL COMMENT '创建时间',
+ `update_user` bigint(0) NULL DEFAULT NULL COMMENT '修改人',
+ `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表';
+INSERT INTO `sys_app` VALUES (639144999463690263, 'ContiNewAdmin', 'admin', '9efcf8859d754288941e61adc72cd250', '1', '2024-10-31 16:53:52', 'ContiNew Admin(Continue New Admin)持续迭代优化的前后端分离中后台管理系统框架。开箱即用,重视每一处代码规范,重视每一种解决方案细节,持续提供舒适的前、后端开发体验。', '1', 1, '2024-10-22 16:54:03', NULL, NULL);
+-- 应用管理菜单数据
+INSERT INTO `sys_menu` VALUES (635516486647025735, '能力开放', 0, 1, '/open', 'Open', 'Layout', NULL, 'expand', b'0', b'0', b'0', NULL, 2, 1, 1, '2024-10-12 16:35:38', 1, '2024-10-12 16:35:53');
+INSERT INTO `sys_menu` VALUES (635516794676711501, '应用管理', 635516486647025735, 2, '/open/app', 'OpenApp', 'open/app/index', NULL, 'common', b'0', b'0', b'0', NULL, 1, 1, 1, '2024-10-12 16:36:51', 1, '2024-10-12 16:49:11');
+INSERT INTO `sys_menu` VALUES (636598391530328174, '新增', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:add', 1, 1, 1, '2024-10-15 16:14:44', 1, '2024-10-15 16:45:36');
+INSERT INTO `sys_menu` VALUES (636599310447808642, '查看', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:list', 999, 1, 1, '2024-10-15 16:18:23', NULL, NULL);
+INSERT INTO `sys_menu` VALUES (636599448054534277, '导出', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:export', 999, 1, 1, '2024-10-15 16:18:56', NULL, NULL);
+INSERT INTO `sys_menu` VALUES (637299919924760580, '删除', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:delete', 1, 1, 1, '2024-10-17 14:42:21', NULL, NULL);
+INSERT INTO `sys_menu` VALUES (637300206014042119, '修改', 635516794676711501, 3, NULL, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'open:app:update', 1, 1, 1, '2024-10-17 14:43:30', NULL, NULL);
+-- 应用管理字典数据
+INSERT INTO `sys_dict` VALUES (639152724557963332, '应用状态', 'app_type', NULL, b'0', 1, '2024-10-22 17:24:44', NULL, NULL);
+INSERT INTO `sys_dict_item` VALUES (639152915579150411, '禁用', '0', 'blue', 999, NULL, 1, 639152724557963332, 1, '2024-10-22 17:25:30', NULL, NULL);
+INSERT INTO `sys_dict_item` VALUES (639153003290435665, '启用', '1', 'green', 999, NULL, 1, 639152724557963332, 1, '2024-10-22 17:25:51', NULL, NULL);
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index fdba62cc..68e7b2cd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
continew-admin-system
continew-admin-common
continew-admin-extension
+ continew-admin-open
@@ -73,6 +74,13 @@
continew-admin-generator
${revision}
+
+
+
+ top.continew
+ continew-admin-open
+ ${revision}
+