diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml
new file mode 100644
index 00000000..c8ec886a
--- /dev/null
+++ b/deploy/docker/docker-compose.yml
@@ -0,0 +1,157 @@
+version: '3'
+
+services:
+ mysql:
+ image: mysql:8.0.33
+ container_name: mysql
+ environment:
+ # 时区上海
+ TZ: Asia/Shanghai
+ # root 密码
+ MYSQL_ROOT_PASSWORD: root
+ # 初始化数据库(后续的初始化sql会在这个库执行)
+ MYSQL_DATABASE: topiam
+ ports:
+ - "3306:3306"
+ volumes:
+ # 数据挂载
+ - /docker/mysql/data/:/var/lib/mysql/
+ # 配置挂载
+ - /docker/mysql/conf/:/etc/mysql/conf.d/
+ command:
+ # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配)
+ --default-authentication-plugin=mysql_native_password
+ --character-set-server=utf8mb4
+ --collation-server=utf8mb4_general_ci
+ --explicit_defaults_for_timestamp=true
+ --lower_case_table_names=1
+ privileged: true
+ network_mode: "host"
+
+ nginx-web:
+ image: nginx:1.23.4
+ container_name: nginx-web
+ environment:
+ # 时区上海
+ TZ: Asia/Shanghai
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ # 证书映射
+ - /docker/nginx/cert:/etc/nginx/cert
+ # 配置文件映射
+ - /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
+ # 页面目录
+ - /docker/nginx/html:/usr/share/nginx/html
+ # 日志目录
+ - /docker/nginx/log:/var/log/nginx
+ privileged: true
+ network_mode: "host"
+
+ redis:
+ image: redis:6.2.12
+ container_name: redis
+ ports:
+ - "6379:6379"
+ environment:
+ # 时区上海
+ TZ: Asia/Shanghai
+ volumes:
+ # 配置文件
+ - /docker/redis/conf:/redis/config:rw
+ # 数据文件
+ - /docker/redis/data/:/redis/data/:rw
+ command: "redis-server /redis/config/redis.conf"
+ privileged: true
+ network_mode: "host"
+
+ elasticsearch:
+ image: elasticsearch:8.7.0
+ container_name: elasticsearch
+ deploy:
+ resources:
+ limits:
+ memory: 2048M
+ volumes:
+ - /docker/elasticsearch:/usr/share/elasticsearch/data
+ ports:
+ - "9200:9200"
+ environment:
+ - node.name=es
+ - cluster.name=elasticsearch
+ - discovery.type=single-node
+ - bootstrap.memory_lock=true
+ - xpack.security.enabled=false
+ - xpack.security.http.ssl.enabled=false
+ - xpack.security.transport.ssl.enabled=false
+ privileged: true
+ network_mode: "host"
+ mem_limit: 1073741824
+ ulimits:
+ memlock:
+ soft: -1
+ hard: -1
+
+ rabbitmq:
+ image: rabbitmq:3.12-management
+ container_name: rabbitmq
+ deploy:
+ resources:
+ limits:
+ memory: 2048M
+ ports:
+ - 5672:5672
+ - 15672:15672
+ environment:
+ RABBITMQ_DEFAULT_USER: rabbit
+ RABBITMQ_DEFAULT_PASS: 123456
+ privileged: true
+ network_mode: "host"
+ volumes:
+ - /docker/rabbitmq/data:/var/lib/rabbitmq
+
+ topiam-console-ce:
+ image: ccr.ccs.tencentyun.com/topiam/eiam-console-ce:1.0.1
+ container_name: topiam-console-ce
+ environment:
+ # 时区上海
+ TZ: Asia/Shanghai
+ SERVER_PORT: 1898
+ ports:
+ - "1898:1898"
+ volumes:
+ # 配置文件
+ - /docker/topiam/conf:/opt/topiam/conf
+ privileged: true
+ network_mode: "host"
+
+ topiam-portal-ce:
+ image: ccr.ccs.tencentyun.com/topiam/eiam-portal-ce:1.0.1
+ container_name: topiam-portal-ce
+ environment:
+ # 时区上海
+ TZ: Asia/Shanghai
+ SERVER_PORT: 1989
+ ports:
+ - "1989:1989"
+ volumes:
+ # 配置文件
+ - /docker/topiam/conf:/opt/topiam/conf
+ privileged: true
+ network_mode: "host"
+
+ topiam-portal-openapi:
+ image: ccr.ccs.tencentyun.com/topiam/eiam-openapi-ce:1.0.1
+ container_name: topiam-openapi-ce
+ environment:
+ # 时区上海
+ TZ: Asia/Shanghai
+ SERVER_PORT: 1988
+ ports:
+ - "1988:1988"
+ volumes:
+ # 配置文件
+ - /docker/topiam/conf:/opt/topiam/conf
+ privileged: true
+ network_mode: "host"
diff --git a/deploy/docker/elasticsearch/README.md b/deploy/docker/elasticsearch/README.md
new file mode 100644
index 00000000..82e7d787
--- /dev/null
+++ b/deploy/docker/elasticsearch/README.md
@@ -0,0 +1 @@
+数据目录 请执行 `chmod 777 /docker/elasticsearch` 赋予读写权限 否则将无法写入数据
\ No newline at end of file
diff --git a/deploy/docker/nginx/conf/nginx.conf b/deploy/docker/nginx/conf/nginx.conf
new file mode 100644
index 00000000..6872a55a
--- /dev/null
+++ b/deploy/docker/nginx/conf/nginx.conf
@@ -0,0 +1,86 @@
+worker_processes 1;
+
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include mime.types;
+ default_type application/octet-stream;
+ sendfile on;
+ keepalive_timeout 65;
+ # 限制body大小
+ client_max_body_size 100m;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ upstream console-server {
+ server 127.0.0.1:1898;
+ }
+
+ upstream portal-server {
+ server 127.0.0.1:1989;
+ }
+
+ upstream openapi-server {
+ server 127.0.0.1:1988;
+ }
+
+ server {
+ listen 80;
+ server_name localhost;
+
+ # https配置参考 start
+ #listen 443 ssl;
+
+ # 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径
+ #ssl on;
+ #ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改
+ #ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改
+ #ssl_session_timeout 5m;
+ #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
+ #ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ #ssl_prefer_server_ciphers on;
+ # https配置参考 end
+
+ # 演示环境配置 拦截除 GET POST 之外的所有请求
+ # if ($request_method !~* GET|POST) {
+ # rewrite ^/(.*)$ /403;
+ # }
+
+ # location = /403 {
+ # default_type application/json;
+ # return 200 '{"msg":"演示模式,不允许操作","code":500}';
+ # }
+
+ # 限制外网访问内网 actuator 相关路径
+ location ~ ^(/[^/]*)?/actuator(/.*)?$ {
+ return 403;
+ }
+
+ location / {
+ root /usr/share/nginx/html; # docker映射路径 不允许更改
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header REMOTE-HOST $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ # websocket参数
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_pass http://console-server/;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root html;
+ }
+ }
+}
diff --git a/deploy/docker/rabbitmq/conf/conf.d/10-defaults.conf b/deploy/docker/rabbitmq/conf/conf.d/10-defaults.conf
new file mode 100644
index 00000000..27973c74
--- /dev/null
+++ b/deploy/docker/rabbitmq/conf/conf.d/10-defaults.conf
@@ -0,0 +1,12 @@
+## DEFAULT SETTINGS ARE NOT MEANT TO BE TAKEN STRAIGHT INTO PRODUCTION
+## see https://www.rabbitmq.com/configure.html for further information
+## on configuring RabbitMQ
+
+## allow access to the guest user from anywhere on the network
+## https://www.rabbitmq.com/access-control.html#loopback-users
+## https://www.rabbitmq.com/production-checklist.html#users
+loopback_users.guest = false
+
+## Send all logs to stdout/TTY. Necessary to see logs when running via
+## a container
+log.console = true
diff --git a/deploy/docker/rabbitmq/conf/enabled_plugins b/deploy/docker/rabbitmq/conf/enabled_plugins
new file mode 100644
index 00000000..cc972a47
--- /dev/null
+++ b/deploy/docker/rabbitmq/conf/enabled_plugins
@@ -0,0 +1 @@
+[rabbitmq_management,rabbitmq_prometheus].
diff --git a/deploy/docker/rabbitmq/data/README.md b/deploy/docker/rabbitmq/data/README.md
new file mode 100644
index 00000000..fbc5474b
--- /dev/null
+++ b/deploy/docker/rabbitmq/data/README.md
@@ -0,0 +1 @@
+数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据
\ No newline at end of file
diff --git a/deploy/docker/redis/conf/redis.conf b/deploy/docker/redis/conf/redis.conf
new file mode 100644
index 00000000..75cebecb
--- /dev/null
+++ b/deploy/docker/redis/conf/redis.conf
@@ -0,0 +1,29 @@
+# redis 密码
+requirepass topiam123
+
+# key 监听器配置
+# notify-keyspace-events Ex
+
+# 配置持久化文件存储路径
+dir /redis/data
+# 配置rdb
+# 15分钟内有至少1个key被更改则进行快照
+save 900 1
+# 5分钟内有至少10个key被更改则进行快照
+save 300 10
+# 1分钟内有至少10000个key被更改则进行快照
+save 60 10000
+# 开启压缩
+rdbcompression yes
+# rdb文件名 用默认的即可
+dbfilename dump.rdb
+
+# 开启aof
+appendonly yes
+# 文件名
+appendfilename "appendonly.aof"
+# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢
+# appendfsync always
+appendfsync everysec
+# appendfsync no
+bind 0.0.0.0
\ No newline at end of file
diff --git a/deploy/docker/redis/data/data/README.md b/deploy/docker/redis/data/data/README.md
new file mode 100644
index 00000000..e851fdd4
--- /dev/null
+++ b/deploy/docker/redis/data/data/README.md
@@ -0,0 +1 @@
+数据目录 请执行 `chmod 777 /docker/rabbitmq/data` 赋予读写权限 否则将无法写入数据
\ No newline at end of file
diff --git a/deploy/docker/topiam/conf/topiam.properties b/deploy/docker/topiam/conf/topiam.properties
new file mode 100644
index 00000000..c6c5c460
--- /dev/null
+++ b/deploy/docker/topiam/conf/topiam.properties
@@ -0,0 +1,51 @@
+#
+# TopIAM Employee - Employee Identity and Access Management
+# Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+#datasource
+spring.datasource.url=jdbc:mysql://127.0.0.1:3306/topiam?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
+spring.datasource.username=root
+spring.datasource.password=root
+
+#redis
+spring.data.redis.host=127.0.0.1
+spring.data.redis.port=6379
+spring.data.redis.password=topiam123
+
+#elasticsearch
+spring.elasticsearch.uris=http://127.0.0.0:9200
+
+#springdoc
+springdoc.swagger-ui.enabled=true
+
+#rabbitmq
+spring.rabbitmq.host=127.0.0.1
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=rabbit
+spring.rabbitmq.password=123456
+spring.rabbitmq.publisher-confirm-type=correlated
+spring.rabbitmq.publisher-returns=true
+spring.rabbitmq.virtual-host=/
+spring.rabbitmq.dynamic=true
+spring.rabbitmq.listener.simple.acknowledge-mode=manual
+
+
+# server config
+topiam.server.console-public-base-url=http://127.0.0.1:1898
+topiam.server.portal-public-base-url=http://127.0.0.1:1989
+topiam.server.openapi-public-base-url=http://127.0.0.1:1988
+topiam.server.synchronizer-public-base-url: http://127.0.0.1:1986
diff --git a/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constant/OpenApiV1Constants.java b/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constant/OpenApiV1Constants.java
index e6c02277..b02b8ab6 100644
--- a/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constant/OpenApiV1Constants.java
+++ b/eiam-openapi/src/main/java/cn/topiam/employee/openapi/constant/OpenApiV1Constants.java
@@ -57,4 +57,9 @@ public class OpenApiV1Constants {
* 组织
*/
public final static String ORGANIZATION_PATH = ACCOUNT_PATH + "/organization";
+
+ /**
+ * 组织
+ */
+ public final static String APP_ACCOUNT_PATH = ACCOUNT_PATH + "/app/account";
}
diff --git a/eiam-openapi/src/main/java/cn/topiam/employee/openapi/endpoint/app/AppAccountController.java b/eiam-openapi/src/main/java/cn/topiam/employee/openapi/endpoint/app/AppAccountController.java
new file mode 100644
index 00000000..ef87f35c
--- /dev/null
+++ b/eiam-openapi/src/main/java/cn/topiam/employee/openapi/endpoint/app/AppAccountController.java
@@ -0,0 +1,86 @@
+package cn.topiam.employee.openapi.endpoint.app;
+
+import cn.topiam.employee.audit.annotation.Audit;
+import cn.topiam.employee.audit.event.type.EventType;
+import cn.topiam.employee.common.entity.app.query.AppAccountQuery;
+import cn.topiam.employee.openapi.common.OpenApiResponse;
+import cn.topiam.employee.openapi.pojo.result.app.AppAccountListResult;
+import cn.topiam.employee.openapi.pojo.save.app.AppAccountCreateParam;
+import cn.topiam.employee.openapi.service.app.AppAccountService;
+import cn.topiam.employee.support.lock.Lock;
+import cn.topiam.employee.support.preview.Preview;
+import cn.topiam.employee.support.repository.page.domain.Page;
+import cn.topiam.employee.support.repository.page.domain.PageModel;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.topiam.employee.openapi.constant.OpenApiV1Constants.APP_ACCOUNT_PATH;
+
+/**
+ * 应用账户
+ *
+ * @author xlsea
+ * @since 2024-01-15
+ */
+@Validated
+@Tag(name = "应用账户")
+@RestController
+@RequiredArgsConstructor
+@RequestMapping(value = APP_ACCOUNT_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
+public class AppAccountController {
+
+ /**
+ * 获取应用账户列表
+ *
+ * @param page {@link PageModel}
+ * @param query {@link AppAccountQuery}
+ * @return {@link AppAccountListResult}
+ */
+ @Operation(summary = "获取应用账户列表")
+ @GetMapping(value = "/list")
+ public OpenApiResponse> getAppAccountList(PageModel page,
+ @Validated AppAccountQuery query) {
+ return OpenApiResponse.success((appAccountService.getAppAccountList(page, query)));
+ }
+
+ /**
+ * 创建应用账户
+ *
+ * @param param {@link AppAccountCreateParam}
+ * @return {@link Boolean}
+ */
+ @Lock
+ @Preview
+ @PostMapping(value = "/create")
+ @Audit(type = EventType.ADD_APP_ACCOUNT)
+ @Operation(summary = "创建应用账户")
+ public OpenApiResponse createAppAccount(@RequestBody @Validated AppAccountCreateParam param) {
+ appAccountService.createAppAccount(param);
+ return OpenApiResponse.success();
+ }
+
+ /**
+ * 删除应用账户
+ *
+ * @param id {@link String}
+ * @return {@link Boolean}
+ */
+ @Lock
+ @Preview
+ @Operation(summary = "删除应用账户")
+ @Audit(type = EventType.DELETE_APP_ACCOUNT)
+ @DeleteMapping(value = "/delete/{id}")
+ public OpenApiResponse deleteAppAccount(@PathVariable(value = "id") String id) {
+ appAccountService.deleteAppAccount(id);
+ return OpenApiResponse.success();
+ }
+
+ /**
+ * 应用账户服务类
+ */
+ private final AppAccountService appAccountService;
+}