diff --git a/README.md b/README.md
index 871f59d0..2e3f922f 100644
--- a/README.md
+++ b/README.md
@@ -282,7 +282,7 @@
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 6.1.10 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?zt) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 6.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?zt) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 8.0.1 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?zt) |
-| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](https://doc.iocoder.cn/bpm/) |
+| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 7.0.0 | [文档](http://172.16.46.63:30888/bpm/) |
| [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 4.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?zt) |
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 9.0.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?zt) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?zt) |
diff --git a/deployment.yaml b/deployment.yaml
index 523a1c8d..63c885e7 100644
--- a/deployment.yaml
+++ b/deployment.yaml
@@ -422,3 +422,77 @@ spec:
port: 48100
targetPort: 48100
nodePort: 30090
+
+
+---
+# zt-module-template
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ namespace: ns-d6a0e78ebd674c279614498e4c57b133
+ name: zt-module-template
+ labels:
+ app: zt-module-template
+ annotations:
+ version: "VERSION_PLACEHOLDER"
+ description: DESC_PLACEHOLDER
+ rollout.kubernetes.io/change-cause: "DESC_PLACEHOLDER:VERSION_PLACEHOLDER"
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: zt-module-template
+ template:
+ metadata:
+ labels:
+ app: zt-module-template
+ spec:
+ containers:
+ - name: zt-module-template
+ image: 172.16.46.66:10043/zt/zt-module-template:VERSION_PLACEHOLDER
+ imagePullPolicy: Always
+ env:
+ - name: TZ
+ value: Asia/Shanghai
+ readinessProbe:
+ httpGet:
+ path: /actuator/health
+ port: 49100
+ initialDelaySeconds: 50
+ periodSeconds: 5
+ failureThreshold: 3
+ livenessProbe:
+ httpGet:
+ path: /actuator/health
+ port: 49100
+ initialDelaySeconds: 50
+ periodSeconds: 10
+ failureThreshold: 5
+ resources:
+ requests:
+ cpu: "500m"
+ memory: "1024Mi"
+ limits:
+ cpu: "700m"
+ memory: "1024Mi"
+ terminationGracePeriodSeconds: 30
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+---
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: ns-d6a0e78ebd674c279614498e4c57b133
+ name: zt-module-template
+spec:
+ type: NodePort
+ selector:
+ app: zt-module-template
+ ports:
+ - protocol: TCP
+ port: 49100
+ targetPort: 49100
+ nodePort: 30889
diff --git a/sql/dm/ruoyi-vue-pro-dm8.sql b/sql/dm/ruoyi-vue-pro-dm8.sql
index 2fba3fe4..aa082349 100644
--- a/sql/dm/ruoyi-vue-pro-dm8.sql
+++ b/sql/dm/ruoyi-vue-pro-dm8.sql
@@ -2800,7 +2800,7 @@ COMMENT ON TABLE system_oauth2_client IS 'OAuth2 客户端表';
-- ----------------------------
-- @formatter:off
-- SET IDENTITY_INSERT system_oauth2_client ON;
-INSERT INTO system_oauth2_client (id, client_id, secret, name, logo, description, status, access_token_validity_seconds, refresh_token_validity_seconds, redirect_uris, authorized_grant_types, scopes, auto_approve_scopes, authorities, resource_ids, additional_information, creator, create_time, updater, update_time, deleted) VALUES (1, 'default', 'admin123', '后台管理', 'http://test.zt.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '["https://www.iocoder.cn","https://doc.iocoder.cn"]', '["password","authorization_code","implicit","refresh_token"]', '["user.read","user.write"]', '[]', '["user.read","user.write"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', '0');
+INSERT INTO system_oauth2_client (id, client_id, secret, name, logo, description, status, access_token_validity_seconds, refresh_token_validity_seconds, redirect_uris, authorized_grant_types, scopes, auto_approve_scopes, authorities, resource_ids, additional_information, creator, create_time, updater, update_time, deleted) VALUES (1, 'default', 'admin123', '后台管理', 'http://test.zt.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '["https://www.iocoder.cn","http://172.16.46.63:30888"]', '["password","authorization_code","implicit","refresh_token"]', '["user.read","user.write"]', '[]', '["user.read","user.write"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', '0');
INSERT INTO system_oauth2_client (id, client_id, secret, name, logo, description, status, access_token_validity_seconds, refresh_token_validity_seconds, redirect_uris, authorized_grant_types, scopes, auto_approve_scopes, authorities, resource_ids, additional_information, creator, create_time, updater, update_time, deleted) VALUES (40, 'test', 'test2', 'biubiu', 'http://test.zt.iocoder.cn/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.jpg', '啦啦啦啦', 0, 1800, 43200, '["https://www.iocoder.cn"]', '["password","authorization_code","implicit"]', '["user_info","projects"]', '["user_info"]', '[]', '[]', '{}', '1', '2022-05-12 00:28:20', '1', '2025-05-02 19:58:08', '0');
INSERT INTO system_oauth2_client (id, client_id, secret, name, logo, description, status, access_token_validity_seconds, refresh_token_validity_seconds, redirect_uris, authorized_grant_types, scopes, auto_approve_scopes, authorities, resource_ids, additional_information, creator, create_time, updater, update_time, deleted) VALUES (41, 'zt-sso-demo-by-code', 'test', '基于授权码模式,如何实现 SSO 单点登录?', 'http://test.zt.iocoder.cn/it/20250502/sign_1746181948685.png', NULL, 0, 1800, 43200, '["http://127.0.0.1:18080"]', '["authorization_code","refresh_token"]', '["user.read","user.write"]', '[]', '[]', '[]', NULL, '1', '2022-09-29 13:28:31', '1', '2025-05-02 18:32:30', '0');
INSERT INTO system_oauth2_client (id, client_id, secret, name, logo, description, status, access_token_validity_seconds, refresh_token_validity_seconds, redirect_uris, authorized_grant_types, scopes, auto_approve_scopes, authorities, resource_ids, additional_information, creator, create_time, updater, update_time, deleted) VALUES (42, 'zt-sso-demo-by-password', 'test', '基于密码模式,如何实现 SSO 单点登录?', 'http://test.zt.iocoder.cn/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.jpeg', NULL, 0, 1800, 43200, '["http://127.0.0.1:18080"]', '["password","refresh_token"]', '["user.read","user.write"]', '[]', '[]', '[]', NULL, '1', '2022-10-04 17:40:16', '1', '2025-05-04 16:00:46', '0');
@@ -4453,6 +4453,7 @@ CREATE TABLE system_users (
username varchar(30) NOT NULL,
password varchar(100) DEFAULT '' NULL,
nickname varchar(30) NOT NULL,
+ workcode varchar(64) DEFAULT NULL NULL,
remark varchar(500) DEFAULT NULL NULL,
post_ids varchar(255) DEFAULT NULL NULL,
email varchar(50) DEFAULT '' NULL,
@@ -4474,6 +4475,7 @@ COMMENT ON COLUMN system_users.id IS '用户ID';
COMMENT ON COLUMN system_users.username IS '用户账号';
COMMENT ON COLUMN system_users.password IS '密码';
COMMENT ON COLUMN system_users.nickname IS '用户昵称';
+COMMENT ON COLUMN system_users.workcode IS '工号';
COMMENT ON COLUMN system_users.remark IS '备注';
COMMENT ON COLUMN system_users.post_ids IS '岗位编号数组';
COMMENT ON COLUMN system_users.email IS '用户邮箱';
@@ -4496,24 +4498,24 @@ COMMENT ON TABLE system_users IS '用户信息表';
-- ----------------------------
-- @formatter:off
-- SET IDENTITY_INSERT system_users ON;
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'admin', '$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '后台管理', '管理员', '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.zt.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, 'zt', '$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', 'ZT', '不要吓我', '[1]', 'zt@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, '[1,2]', '111@qq.com', '15601691200', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', 'ZT', NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2025-04-21 14:23:08', '0', 118);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', 'ZT', NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2025-04-21 14:23:08', '0', 119);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', 'ZT', NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2025-04-21 14:23:08', '0', 120);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, 'admin110', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '小王', NULL, NULL, '', '15601691300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-07-20 22:23:17', '1', '2022-02-22 00:56:14', NULL, '2025-04-21 14:23:08', '0', 121);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, 'test', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '测试用户', NULL, '[]', '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2023-12-30 11:42:17', '110', '2022-02-23 13:14:33', NULL, '2025-04-21 14:23:08', '0', 121);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 'newobject', '$2a$04$dB0z8Q819fJWz0hbaLe6B.VfHCjYgWx6LFfET5lyz3JwcqlyCkQ4C', '新对象', NULL, '[]', '', '15601691235', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-16 23:11:38', '1', '2022-02-23 19:08:03', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', 'ZT1', NULL, NULL, '', '15601691300', 0, NULL, 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', '1', '2025-05-05 15:30:53', '0', 122);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (114, 'hrmgr', '$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', 'hr 小姐姐', NULL, '[5]', '', '15601691236', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 'aotemane', '$2a$04$GcyP0Vyzb2F2Yni5PuIK9ueGxM0tkZGMtDwVRwrNbtMvorzbpNsV2', '阿呆', '11222', '[1,2]', '7648@qq.com', '15601691229', 2, NULL, 0, '', NULL, '1', '2022-04-30 02:55:43', '1', '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 'admin123', '$2a$04$sEtimsHu9YCkYY4/oqElHem2Ijc9ld20eYO6lN.g/21NfLUTDLB9W', '测试号02', '1111', '[2]', '', '15601691234', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-10-02 10:16:20', '1', '2022-07-09 17:40:26', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (118, 'goudan', '$2a$04$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', '2024-04-27 08:45:56', '1', '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (139, 'wwbwwb', '$2a$04$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', '0', 1);
-INSERT INTO system_users (id, username, password, nickname, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (141, 'admin1', '$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', '新用户', NULL, NULL, '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'admin', '$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '后台管理', NULL, '管理员', '[1,2]', '11aoteman@126.com', '18818260277', 2, 'http://test.zt.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, 'zt', '$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', 'ZT', NULL, '不要吓我', '[1]', 'zt@iocoder.cn', '15601691300', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, NULL, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, NULL, '[1,2]', '111@qq.com', '15601691200', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', 'ZT', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2025-04-21 14:23:08', '0', 118);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', 'ZT', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2025-04-21 14:23:08', '0', 119);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', 'ZT', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2025-04-21 14:23:08', '0', 120);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (110, 'admin110', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '小王', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-07-20 22:23:17', '1', '2022-02-22 00:56:14', NULL, '2025-04-21 14:23:08', '0', 121);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (111, 'test', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '测试用户', NULL, NULL, '[]', '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2023-12-30 11:42:17', '110', '2022-02-23 13:14:33', NULL, '2025-04-21 14:23:08', '0', 121);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (112, 'newobject', '$2a$04$dB0z8Q819fJWz0hbaLe6B.VfHCjYgWx6LFfET5lyz3JwcqlyCkQ4C', '新对象', NULL, NULL, '[]', '', '15601691235', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-16 23:11:38', '1', '2022-02-23 19:08:03', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', 'ZT1', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', '1', '2025-05-05 15:30:53', '0', 122);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (114, 'hrmgr', '$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', 'hr 小姐姐', NULL, NULL, '[5]', '', '15601691236', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-24 22:21:05', '1', '2022-03-19 21:50:58', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (115, 'aotemane', '$2a$04$GcyP0Vyzb2F2Yni5PuIK9ueGxM0tkZGMtDwVRwrNbtMvorzbpNsV2', '阿呆', NULL, '11222', '[1,2]', '7648@qq.com', '15601691229', 2, NULL, 0, '', NULL, '1', '2022-04-30 02:55:43', '1', '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (117, 'admin123', '$2a$04$sEtimsHu9YCkYY4/oqElHem2Ijc9ld20eYO6lN.g/21NfLUTDLB9W', '测试号02', NULL, '1111', '[2]', '', '15601691234', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-10-02 10:16:20', '1', '2022-07-09 17:40:26', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (118, 'goudan', '$2a$04$jth0yOj8cSJq84D6vrzusOHDwW/LpBfgBnQ6bfFlD8zNZfM632Ta2', '狗蛋', NULL, NULL, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-17 09:10:27', '1', '2022-07-09 17:44:43', '1', '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (131, 'hh', '$2a$04$jyH9h6.gaw8mpOjPfHIpx.8as2Rzfcmdlj5rlJFwgCw4rsv/MTb2K', '呵呵', NULL, NULL, '[]', '777@qq.com', '15601882312', 1, NULL, 0, '', NULL, '1', '2024-04-27 08:45:56', '1', '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (139, 'wwbwwb', '$2a$04$aOHoFbQU6zfBk/1Z9raF/ugTdhjNdx7culC1HhO0zvoczAnahCiMq', '小秃头', NULL, NULL, NULL, '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', NULL, '2025-04-21 14:23:08', '0', 1);
+INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (141, 'admin1', '$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', '新用户', NULL, NULL, NULL, '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '0', 1);
COMMIT;
-- SET IDENTITY_INSERT system_users OFF;
-- @formatter:on
diff --git a/sql/dm/新增用户工号字段_20251202.sql b/sql/dm/新增用户工号字段_20251202.sql
new file mode 100644
index 00000000..138e864a
--- /dev/null
+++ b/sql/dm/新增用户工号字段_20251202.sql
@@ -0,0 +1,8 @@
+-- 达梦8数据库 DDL 脚本
+-- 为 system_users 表添加 workcode 字段(工号)
+
+-- 添加工号字段
+ALTER TABLE system_users ADD COLUMN workcode VARCHAR(64);
+
+-- 添加字段注释
+COMMENT ON COLUMN system_users.workcode IS '工号';
diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql
index 6fdd4399..2e036edc 100644
--- a/sql/mysql/ruoyi-vue-pro.sql
+++ b/sql/mysql/ruoyi-vue-pro.sql
@@ -2450,7 +2450,7 @@ CREATE TABLE `system_oauth2_client` (
-- Records of system_oauth2_client
-- ----------------------------
BEGIN;
-INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'default', 'admin123', '后台管理', 'http://test.zt.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '[\"https://www.iocoder.cn\",\"https://doc.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', b'0');
+INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'default', 'admin123', '后台管理', 'http://test.zt.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '[\"https://www.iocoder.cn\",\"http://172.16.46.63:30888\"]', '[\"password\",\"authorization_code\",\"implicit\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-05-02 20:42:22', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (40, 'test', 'test2', 'biubiu', 'http://test.zt.iocoder.cn/xx/20250502/ed07110a37464b5299f8bd7c67ad65c7_1746187077009.jpg', '啦啦啦啦', 0, 1800, 43200, '[\"https://www.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\"]', '[\"user_info\",\"projects\"]', '[\"user_info\"]', '[]', '[]', '{}', '1', '2022-05-12 00:28:20', '1', '2025-05-02 19:58:08', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (41, 'zt-sso-demo-by-code', 'test', '基于授权码模式,如何实现 SSO 单点登录?', 'http://test.zt.iocoder.cn/it/20250502/sign_1746181948685.png', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"authorization_code\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-09-29 13:28:31', '1', '2025-05-02 18:32:30', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (42, 'zt-sso-demo-by-password', 'test', '基于密码模式,如何实现 SSO 单点登录?', 'http://test.zt.iocoder.cn/604bdc695e13b3b22745be704d1f2aa8ee05c5f26f9fead6d1ca49005afbc857.jpeg', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"password\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-10-04 17:40:16', '1', '2025-05-04 16:00:46', b'0');
diff --git a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/enums/DocumentEnum.java b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/enums/DocumentEnum.java
index f7598356..126c2688 100644
--- a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/enums/DocumentEnum.java
+++ b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/enums/DocumentEnum.java
@@ -13,7 +13,7 @@ import lombok.Getter;
public enum DocumentEnum {
REDIS_INSTALL("https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues/I4VCSJ", "Redis 安装文档"),
- TENANT("https://doc.iocoder.cn", "SaaS 多租户文档");
+ TENANT("http://172.16.46.63:30888", "SaaS 多租户文档");
private final String url;
private final String memo;
diff --git a/zt-framework/zt-spring-boot-starter-biz-business/pom.xml b/zt-framework/zt-spring-boot-starter-biz-business/pom.xml
index 86e2487d..a410267a 100644
--- a/zt-framework/zt-spring-boot-starter-biz-business/pom.xml
+++ b/zt-framework/zt-spring-boot-starter-biz-business/pom.xml
@@ -36,6 +36,11 @@
zt-spring-boot-starter-biz-tenant
${revision}
+
+ com.zt.plat
+ zt-spring-boot-starter-mybatis
+ ${revision}
+
com.zt.plat
diff --git a/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionConfiguration.java b/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionConfiguration.java
index 614cf30c..174049ad 100644
--- a/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionConfiguration.java
+++ b/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionConfiguration.java
@@ -2,27 +2,54 @@ package com.zt.plat.framework.business.framework;
import com.zt.plat.framework.datapermission.core.rule.company.CompanyDataPermissionRuleCustomizer;
import com.zt.plat.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
/**
- * @author chenbowen
+ * 自动为继承 BusinessBaseDO 的实体注册公司/部门数据权限字段。
*/
@Configuration(proxyBeanMethods = false)
public class BusinessDataPermissionConfiguration {
+
@Bean
- public CompanyDataPermissionRuleCustomizer sysCompanyDataPermissionRuleCustomizer() {
- return rule -> {
- // companyId
- rule.addCompanyColumn("demo_contract", "company_id");
- };
+ public BusinessDataPermissionEntityScanner businessDataPermissionEntityScanner(BeanFactory beanFactory, ApplicationContext applicationContext) {
+ Set basePackages = new LinkedHashSet<>();
+ if (AutoConfigurationPackages.has(beanFactory)) {
+ basePackages.addAll(AutoConfigurationPackages.get(beanFactory));
+ }
+ if (basePackages.isEmpty()) {
+ basePackages.add("com.zt");
+ }
+ ClassLoader classLoader = applicationContext != null
+ ? applicationContext.getClassLoader()
+ : Thread.currentThread().getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = BusinessDataPermissionConfiguration.class.getClassLoader();
+ }
+ return new BusinessDataPermissionEntityScanner(basePackages, classLoader);
}
@Bean
- public DeptDataPermissionRuleCustomizer businessDeptDataPermissionRuleCustomizer() {
- return rule -> {
- // dept
- rule.addDeptColumn("demo_contract", "dept_id");
- };
+ public CompanyDataPermissionRuleCustomizer autoCompanyDataPermissionRuleCustomizer(BusinessDataPermissionEntityScanner scanner) {
+ return rule -> scanner.getEntityMetadata().forEach(metadata -> {
+ if (metadata.hasCompanyColumn()) {
+ rule.addCompanyColumn(metadata.getTableName(), metadata.getCompanyColumn());
+ }
+ });
+ }
+
+ @Bean
+ public DeptDataPermissionRuleCustomizer autoDeptDataPermissionRuleCustomizer(BusinessDataPermissionEntityScanner scanner) {
+ return rule -> scanner.getEntityMetadata().forEach(metadata -> {
+ if (metadata.hasDeptColumn()) {
+ rule.addDeptColumn(metadata.getTableName(), metadata.getDeptColumn());
+ }
+ });
}
}
diff --git a/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionEntityScanner.java b/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionEntityScanner.java
new file mode 100644
index 00000000..93c306f5
--- /dev/null
+++ b/zt-framework/zt-spring-boot-starter-biz-business/src/main/java/com/zt/plat/framework/business/framework/BusinessDataPermissionEntityScanner.java
@@ -0,0 +1,159 @@
+package com.zt.plat.framework.business.framework;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.zt.plat.framework.mybatis.core.annotation.CompanyColumn;
+import com.zt.plat.framework.mybatis.core.annotation.DeptColumn;
+import com.zt.plat.framework.mybatis.core.dataobject.BusinessBaseDO;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * 扫描继承 {@link BusinessBaseDO} 的实体,自动提取公司/部门字段用于数据权限注册。
+ *
+ * @author chenbow
+ */
+@Slf4j
+public class BusinessDataPermissionEntityScanner {
+
+ private final Set basePackages;
+ private final ClassLoader classLoader;
+
+ private volatile List cachedEntities;
+
+ public BusinessDataPermissionEntityScanner(Collection basePackages, ClassLoader classLoader) {
+ Set packages = new LinkedHashSet<>();
+ if (!CollectionUtils.isEmpty(basePackages)) {
+ packages.addAll(basePackages);
+ }
+ if (packages.isEmpty()) {
+ packages.add("com.zt");
+ }
+ this.basePackages = Collections.unmodifiableSet(packages);
+ this.classLoader = classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader();
+ }
+
+ public List getEntityMetadata() {
+ List result = cachedEntities;
+ if (result == null) {
+ synchronized (this) {
+ result = cachedEntities;
+ if (result == null) {
+ result = Collections.unmodifiableList(scanEntities());
+ cachedEntities = result;
+ }
+ }
+ }
+ return result;
+ }
+
+ private List scanEntities() {
+ Map metadataMap = new LinkedHashMap<>();
+ ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+ scanner.addIncludeFilter(new AssignableTypeFilter(BusinessBaseDO.class));
+
+ for (String basePackage : basePackages) {
+ for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackage)) {
+ String className = beanDefinition.getBeanClassName();
+ if (!StringUtils.hasText(className)) {
+ continue;
+ }
+ try {
+ Class> clazz = ClassUtils.forName(className, classLoader);
+ if (clazz == BusinessBaseDO.class || !BusinessBaseDO.class.isAssignableFrom(clazz)) {
+ continue;
+ }
+ if (Modifier.isAbstract(clazz.getModifiers())) {
+ continue;
+ }
+ @SuppressWarnings("unchecked")
+ Class extends BusinessBaseDO> entityClass = (Class extends BusinessBaseDO>) clazz;
+ EntityMetadata metadata = buildMetadata(entityClass);
+ if (metadata != null && StringUtils.hasText(metadata.getTableName())) {
+ metadataMap.putIfAbsent(metadata.getTableName(), metadata);
+ }
+ } catch (ClassNotFoundException ex) {
+ log.warn("[scanEntities][无法加载类 {}]", className, ex);
+ }
+ }
+ }
+ return new ArrayList<>(metadataMap.values());
+ }
+
+ private EntityMetadata buildMetadata(Class extends BusinessBaseDO> entityClass) {
+ String tableName = resolveTableName(entityClass);
+ if (!StringUtils.hasText(tableName)) {
+ log.debug("[buildMetadata][实体 {} 缺少表名配置,跳过自动注册]", entityClass.getName());
+ return null;
+ }
+ String companyColumn = resolveCompanyColumn(entityClass);
+ String deptColumn = resolveDeptColumn(entityClass);
+ if (!StringUtils.hasText(companyColumn) && !StringUtils.hasText(deptColumn)) {
+ log.debug("[buildMetadata][实体 {} 未配置公司/部门字段,跳过]", entityClass.getName());
+ return null;
+ }
+ return new EntityMetadata(tableName, companyColumn, deptColumn);
+ }
+
+ private String resolveTableName(Class> entityClass) {
+ TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
+ if (tableInfo != null && StringUtils.hasText(tableInfo.getTableName())) {
+ return tableInfo.getTableName();
+ }
+ TableName tableName = AnnotatedElementUtils.findMergedAnnotation(entityClass, TableName.class);
+ if (StringUtils.hasText(tableName.value())) {
+ return tableName.value();
+ }
+ // 退化为根据类名猜测(驼峰转下划线)
+ String fallback = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(entityClass.getSimpleName());
+ return StringUtils.hasText(fallback) ? fallback : null;
+ }
+
+ private String resolveCompanyColumn(Class> entityClass) {
+ CompanyColumn annotation = AnnotatedElementUtils.findMergedAnnotation(entityClass, CompanyColumn.class);
+ return annotation.value();
+ }
+
+ private String resolveDeptColumn(Class> entityClass) {
+ DeptColumn annotation = AnnotatedElementUtils.findMergedAnnotation(entityClass, DeptColumn.class);
+ return annotation.value();
+ }
+
+ @Getter
+ @RequiredArgsConstructor
+ public static class EntityMetadata {
+ private final String tableName;
+ private final String companyColumn;
+ private final String deptColumn;
+
+ public boolean hasCompanyColumn() {
+ return StringUtils.hasText(companyColumn);
+ }
+
+ public boolean hasDeptColumn() {
+ return StringUtils.hasText(deptColumn);
+ }
+
+ @Override
+ public String toString() {
+ return "EntityMetadata{" +
+ "table='" + tableName + '\'' +
+ ", company='" + companyColumn + '\'' +
+ ", dept='" + deptColumn + '\'' +
+ '}';
+ }
+
+ }
+}
diff --git a/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/annotation/CompanyColumn.java b/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/annotation/CompanyColumn.java
new file mode 100644
index 00000000..386036db
--- /dev/null
+++ b/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/annotation/CompanyColumn.java
@@ -0,0 +1,23 @@
+package com.zt.plat.framework.mybatis.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标记业务实体对应表中的公司字段名称,默认 company_id。
+ *
+ * @author chenbow
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface CompanyColumn {
+
+ /**
+ * 表中公司字段名称
+ */
+ String value() default "company_id";
+}
diff --git a/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/annotation/DeptColumn.java b/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/annotation/DeptColumn.java
new file mode 100644
index 00000000..d8300aab
--- /dev/null
+++ b/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/annotation/DeptColumn.java
@@ -0,0 +1,23 @@
+package com.zt.plat.framework.mybatis.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标记业务实体对应表中的部门字段名称,默认 dept_id。
+ *
+ * @author chenbow
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DeptColumn {
+
+ /**
+ * 表中部门字段名称
+ */
+ String value() default "dept_id";
+}
diff --git a/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/dataobject/BusinessBaseDO.java b/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/dataobject/BusinessBaseDO.java
index 56252a0e..c983a463 100644
--- a/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/dataobject/BusinessBaseDO.java
+++ b/zt-framework/zt-spring-boot-starter-mybatis/src/main/java/com/zt/plat/framework/mybatis/core/dataobject/BusinessBaseDO.java
@@ -2,6 +2,8 @@ package com.zt.plat.framework.mybatis.core.dataobject;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
+import com.zt.plat.framework.mybatis.core.annotation.CompanyColumn;
+import com.zt.plat.framework.mybatis.core.annotation.DeptColumn;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.ibatis.type.JdbcType;
@@ -13,6 +15,8 @@ import java.util.List;
*/
@Data
@EqualsAndHashCode(callSuper = true)
+@CompanyColumn
+@DeptColumn
public class BusinessBaseDO extends BaseDO {
/** 公司编号 */
diff --git a/zt-framework/zt-spring-boot-starter-web/src/main/java/com/zt/plat/framework/web/core/handler/GlobalExceptionHandler.java b/zt-framework/zt-spring-boot-starter-web/src/main/java/com/zt/plat/framework/web/core/handler/GlobalExceptionHandler.java
index 1d2b8907..edcbfaf9 100644
--- a/zt-framework/zt-spring-boot-starter-web/src/main/java/com/zt/plat/framework/web/core/handler/GlobalExceptionHandler.java
+++ b/zt-framework/zt-spring-boot-starter-web/src/main/java/com/zt/plat/framework/web/core/handler/GlobalExceptionHandler.java
@@ -6,6 +6,7 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.zt.plat.framework.common.biz.infra.logger.ApiErrorLogCommonApi;
import com.zt.plat.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;
import com.zt.plat.framework.common.exception.ServiceException;
@@ -16,7 +17,6 @@ import com.zt.plat.framework.common.util.json.JsonUtils;
import com.zt.plat.framework.common.util.monitor.TracerUtils;
import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
-import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
@@ -365,42 +365,12 @@ public class GlobalExceptionHandler {
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[微信公众号 zt-module-mp - 表结构未导入][参考 http://172.16.46.63:30888/mp/build/ 开启]");
}
- // 4. 商城系统
- if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
- log.error("[商城系统 zt-module-mall - 已禁用][参考 http://172.16.46.63:30888/mall/build/ 开启]");
- return CommonResult.error(NOT_IMPLEMENTED.getCode(),
- "[商城系统 zt-module-mall - 已禁用][参考 http://172.16.46.63:30888/mall/build/ 开启]");
- }
- // 5. ERP 系统
- if (message.contains("erp_")) {
- log.error("[ERP 系统 zt-module-erp - 表结构未导入][参考 http://172.16.46.63:30888/erp/build/ 开启]");
- return CommonResult.error(NOT_IMPLEMENTED.getCode(),
- "[ERP 系统 zt-module-erp - 表结构未导入][参考 http://172.16.46.63:30888/erp/build/ 开启]");
- }
- // 6. CRM 系统
- if (message.contains("crm_")) {
- log.error("[CRM 系统 zt-module-crm - 表结构未导入][参考 http://172.16.46.63:30888/crm/build/ 开启]");
- return CommonResult.error(NOT_IMPLEMENTED.getCode(),
- "[CRM 系统 zt-module-crm - 表结构未导入][参考 http://172.16.46.63:30888/crm/build/ 开启]");
- }
- // 7. 支付平台
- if (message.contains("pay_")) {
- log.error("[支付模块 zt-module-pay - 表结构未导入][参考 http://172.16.46.63:30888/pay/build/ 开启]");
- return CommonResult.error(NOT_IMPLEMENTED.getCode(),
- "[支付模块 zt-module-pay - 表结构未导入][参考 http://172.16.46.63:30888/pay/build/ 开启]");
- }
// 8. AI 大模型
if (message.contains("ai_")) {
log.error("[AI 大模型 zt-module-ai - 表结构未导入][参考 http://172.16.46.63:30888/ai/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[AI 大模型 zt-module-ai - 表结构未导入][参考 http://172.16.46.63:30888/ai/build/ 开启]");
}
- // 9. IOT 物联网
- if (message.contains("iot_")) {
- log.error("[IoT 物联网 zt-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
- return CommonResult.error(NOT_IMPLEMENTED.getCode(),
- "[IoT 物联网 zt-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
- }
return null;
}
diff --git a/zt-framework/zt-spring-boot-starter-web/src/test/java/com/zt/plat/framework/desensitize/core/DesensitizeTest.java b/zt-framework/zt-spring-boot-starter-web/src/test/java/com/zt/plat/framework/desensitize/core/DesensitizeTest.java
index 3b6dc26a..0176695d 100644
--- a/zt-framework/zt-spring-boot-starter-web/src/test/java/com/zt/plat/framework/desensitize/core/DesensitizeTest.java
+++ b/zt-framework/zt-spring-boot-starter-web/src/test/java/com/zt/plat/framework/desensitize/core/DesensitizeTest.java
@@ -48,7 +48,7 @@ public class DesensitizeTest {
DesensitizeDemo d = JsonUtils.parseObject(JsonUtils.toJsonString(desensitizeDemo), DesensitizeDemo.class);
// 断言
assertNotNull(d);
- assertEquals("芋***", d.getNickname());
+ assertEquals("Z***", d.getNickname());
assertEquals("998800********31", d.getBankCard());
assertEquals("粤A6***6", d.getCarLicense());
assertEquals("0108*****22", d.getFixedPhone());
diff --git a/zt-gateway/Dockerfile b/zt-gateway/Dockerfile
index 358c75e0..47876e4d 100644
--- a/zt-gateway/Dockerfile
+++ b/zt-gateway/Dockerfile
@@ -1,7 +1,6 @@
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
-ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:9.7.0
-FROM ${BASE_IMAGE}
+FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
## 创建目录,并使用它作为工作目录
RUN mkdir -p /zt-gateway
@@ -11,15 +10,10 @@ COPY ./target/zt-gateway.jar app.jar
## 设置 TZ 时区
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
-ENV TZ=Asia/Shanghai
-ENV JAVA_OPTS="-Xms512m -Xmx512m"
-ENV SW_AGENT_HOME=/opt/skywalking/agent
-ENV SW_AGENT_NAME=zt-gateway
-ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=172.16.46.63:30201
-ENV AGENT_JAVA_OPTS="-javaagent:${SW_AGENT_HOME}/skywalking-agent.jar -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES}"
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m"
## 暴露后端项目的 48080 端口
EXPOSE 48080
## 启动后端项目
-CMD java ${AGENT_JAVA_OPTS} ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/zt-module-ai/zt-module-ai-server/Dockerfile b/zt-module-ai/zt-module-ai-server/Dockerfile
index c13e8005..246697fb 100644
--- a/zt-module-ai/zt-module-ai-server/Dockerfile
+++ b/zt-module-ai/zt-module-ai-server/Dockerfile
@@ -1,7 +1,6 @@
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
-ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:9.7.0
-FROM ${BASE_IMAGE}
+FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
## 创建目录,并使用它作为工作目录
RUN mkdir -p /zt-module-ai-server
@@ -11,15 +10,10 @@ COPY ./target/zt-module-ai-server.jar app.jar
## 设置 TZ 时区
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
-ENV TZ=Asia/Shanghai
-ENV JAVA_OPTS="-Xms512m -Xmx512m"
-ENV SW_AGENT_HOME=/opt/skywalking/agent
-ENV SW_AGENT_NAME=zt-module-ai-server
-ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=172.16.46.63:30201
-ENV AGENT_JAVA_OPTS="-javaagent:${SW_AGENT_HOME}/skywalking-agent.jar -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES}"
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m"
## 暴露后端项目的 48080 端口
EXPOSE 48090
## 启动后端项目
-CMD java ${AGENT_JAVA_OPTS} ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java b/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
index 38368f0b..dee17fc4 100644
--- a/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
+++ b/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
@@ -33,7 +33,7 @@ public class AiKnowledgeDocumentCreateListReqVO {
@NotBlank(message = "文档名称不能为空")
private String name;
- @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
+ @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://172.16.46.63:30888")
@URL(message = "文档 URL 格式不正确")
private String url;
diff --git a/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
index 0a4249dc..de3bb07b 100644
--- a/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
+++ b/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
@@ -18,7 +18,7 @@ public class AiKnowledgeDocumentRespVO {
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
private String name;
- @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
+ @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://172.16.46.63:30888")
private String url;
@Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
diff --git a/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
index 4da54893..3a1e8fdb 100644
--- a/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
+++ b/zt-module-ai/zt-module-ai-server/src/main/java/com/zt/plat/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
@@ -19,7 +19,7 @@ public class AiKnowledgeDocumentCreateReqVO {
@NotBlank(message = "文档名称不能为空")
private String name;
- @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
+ @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://172.16.46.63:30888")
@URL(message = "文档 URL 格式不正确")
private String url;
diff --git a/zt-module-bpm/zt-module-bpm-server/Dockerfile b/zt-module-bpm/zt-module-bpm-server/Dockerfile
index eceda19f..868eec5c 100644
--- a/zt-module-bpm/zt-module-bpm-server/Dockerfile
+++ b/zt-module-bpm/zt-module-bpm-server/Dockerfile
@@ -1,7 +1,6 @@
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
-ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:9.7.0
-FROM ${BASE_IMAGE}
+FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
## 创建目录,并使用它作为工作目录
RUN mkdir -p /zt-module-bpm-server
@@ -11,15 +10,10 @@ COPY ./target/zt-module-bpm-server.jar app.jar
## 设置 TZ 时区
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
-ENV TZ=Asia/Shanghai
-ENV JAVA_OPTS="-Xms512m -Xmx512m"
-ENV SW_AGENT_HOME=/opt/skywalking/agent
-ENV SW_AGENT_NAME=zt-module-bpm-server
-ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=172.16.46.63:30201
-ENV AGENT_JAVA_OPTS="-javaagent:${SW_AGENT_HOME}/skywalking-agent.jar -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES}"
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m"
## 暴露后端项目的 48080 端口
EXPOSE 48083
## 启动后端项目
-CMD java ${AGENT_JAVA_OPTS} ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/config/GatewayWebClientConfiguration.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/config/GatewayWebClientConfiguration.java
index 24811a53..96ba5a07 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/config/GatewayWebClientConfiguration.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/config/GatewayWebClientConfiguration.java
@@ -27,7 +27,7 @@ public class GatewayWebClientConfiguration {
@Value("${databus.gateway.web-client.max-in-memory-size:20971520}") int maxInMemorySize,
@Value("${databus.gateway.web-client.max-idle-time:45000}") long maxIdleTimeMillis,
@Value("${databus.gateway.web-client.evict-in-background-interval:20000}") long evictInBackgroundMillis,
- @Value("${databus.gateway.web-client.connection-pool-enabled:true}") boolean connectionPoolEnabled) {
+ @Value("${databus.gateway.web-client.connection-pool-enabled:false}") boolean connectionPoolEnabled) {
this.maxInMemorySize = maxInMemorySize;
this.maxIdleTimeMillis = maxIdleTimeMillis;
this.evictInBackgroundMillis = evictInBackgroundMillis;
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java
index 8528c16d..bce32b79 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java
@@ -3,9 +3,12 @@ package com.zt.plat.module.databus.framework.integration.gateway.core;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.framework.common.util.monitor.TracerUtils;
+import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import com.zt.plat.module.databus.framework.integration.gateway.security.CachedBodyHttpServletRequest;
import com.zt.plat.module.databus.service.gateway.ApiAccessLogService;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
@@ -20,8 +23,7 @@ import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
/**
* 将 API 调用上下文持久化为访问日志。
@@ -33,6 +35,8 @@ public class ApiGatewayAccessLogger {
public static final String ATTR_LOG_ID = "ApiAccessLogId";
public static final String ATTR_EXCEPTION_STACK = "ApiAccessLogExceptionStack";
+ public static final String HEADER_ACCESS_LOG_ID = "X-Databus-AccessLog-Id";
+ private static final String ATTR_REQUEST_START = "ApiAccessLogRequestStart";
private static final int MAX_TEXT_LENGTH = 4000;
@@ -44,24 +48,15 @@ public class ApiGatewayAccessLogger {
*/
public void onRequest(ApiInvocationContext context) {
try {
- String traceId = TracerUtils.getTraceId();
- ApiAccessLogDO logDO = new ApiAccessLogDO();
- logDO.setTraceId(traceId);
- logDO.setApiCode(context.getApiCode());
- logDO.setApiVersion(context.getApiVersion());
- logDO.setRequestMethod(context.getHttpMethod());
- logDO.setRequestPath(context.getRequestPath());
- logDO.setRequestQuery(toJson(context.getRequestQueryParams()));
- logDO.setRequestHeaders(toJson(context.getRequestHeaders()));
- logDO.setRequestBody(toJson(context.getRequestBody()));
- logDO.setClientIp(firstNonBlank(context.getClientIp(),
- GatewayHeaderUtils.findFirstHeaderValue(context.getRequestHeaders(), "X-Forwarded-For")));
- logDO.setUserAgent(GatewayHeaderUtils.findFirstHeaderValue(context.getRequestHeaders(), HttpHeaders.USER_AGENT));
- logDO.setStatus(3); // 默认未知
- logDO.setRequestTime(toLocalDateTime(context.getRequestTime()));
- logDO.setTenantId(parseTenantId(context.getTenantId()));
- Long logId = apiAccessLogService.create(logDO);
- context.getAttributes().put(ATTR_LOG_ID, logId);
+ ApiAccessLogDO logDO = buildRequestSnapshot(context);
+ Long existingLogId = getLogId(context);
+ if (existingLogId != null) {
+ logDO.setId(existingLogId);
+ apiAccessLogService.update(logDO);
+ } else {
+ Long logId = apiAccessLogService.create(logDO);
+ context.getAttributes().put(ATTR_LOG_ID, logId);
+ }
} catch (Exception ex) {
log.warn("记录 API 访问日志开始阶段失败, traceId={}", TracerUtils.getTraceId(), ex);
}
@@ -111,15 +106,97 @@ public class ApiGatewayAccessLogger {
}
}
+ /**
+ * 安全过滤阶段的第一时间记录请求元数据,保证被快速拒绝的请求也能查询。
+ */
+ public Long logEntrance(HttpServletRequest request) {
+ if (request == null) {
+ return null;
+ }
+ Object existing = request.getAttribute(ATTR_LOG_ID);
+ if (existing instanceof Long logId) {
+ return logId;
+ }
+ try {
+ ApiAccessLogDO logDO = new ApiAccessLogDO();
+ logDO.setTraceId(TracerUtils.getTraceId());
+ logDO.setRequestMethod(request.getMethod());
+ logDO.setRequestPath(request.getRequestURI());
+ logDO.setRequestQuery(truncate(request.getQueryString()));
+ logDO.setRequestHeaders(toJson(collectHeaders(request)));
+ logDO.setClientIp(ServletUtils.getClientIP(request));
+ logDO.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
+ logDO.setStatus(3);
+ logDO.setRequestTime(LocalDateTime.now());
+ Long logId = apiAccessLogService.create(logDO);
+ request.setAttribute(ATTR_LOG_ID, logId);
+ request.setAttribute(ATTR_REQUEST_START, Instant.now());
+ return logId;
+ } catch (Exception ex) {
+ log.warn("记录入口 API 访问日志失败", ex);
+ return null;
+ }
+ }
+
+ /**
+ * 编排前即结束的请求在此补写状态码、耗时等关键信息。
+ */
+ public void finalizeEarly(HttpServletRequest request, int status, String message) {
+ if (request == null) {
+ return;
+ }
+ Object existing = request.getAttribute(ATTR_LOG_ID);
+ if (!(existing instanceof Long logId)) {
+ return;
+ }
+ try {
+ ApiAccessLogDO update = new ApiAccessLogDO();
+ update.setId(logId);
+ update.setResponseStatus(status);
+ update.setResponseMessage(truncate(message));
+ update.setStatus(resolveStatus(status));
+ update.setResponseTime(LocalDateTime.now());
+ update.setDuration(calculateDuration(request));
+ apiAccessLogService.update(update);
+ } catch (Exception ex) {
+ log.warn("更新入口 API 访问日志失败, logId={}", logId, ex);
+ }
+ }
+
+ /**
+ * 将入口阶段生成的 logId 通过请求头继续传递,供后续流程关联合并。
+ */
+ public static void propagateLogIdHeader(CachedBodyHttpServletRequest requestWrapper, Long logId) {
+ if (requestWrapper == null || logId == null) {
+ return;
+ }
+ requestWrapper.setHeader(HEADER_ACCESS_LOG_ID, String.valueOf(logId));
+ }
+
private Long getLogId(ApiInvocationContext context) {
Object value = context.getAttributes().get(ATTR_LOG_ID);
- if (value instanceof Long) {
- return (Long) value;
- }
- if (value instanceof Number number) {
- return number.longValue();
- }
- return null;
+ return value instanceof Long ? (Long) value : null;
+ }
+
+ /**
+ * 根据编排上下文构建请求侧快照,用于访问日志首段信息。
+ */
+ private ApiAccessLogDO buildRequestSnapshot(ApiInvocationContext context) {
+ ApiAccessLogDO logDO = new ApiAccessLogDO();
+ logDO.setTraceId(TracerUtils.getTraceId());
+ logDO.setApiCode(context.getApiCode());
+ logDO.setApiVersion(context.getApiVersion());
+ logDO.setRequestMethod(context.getHttpMethod());
+ logDO.setRequestPath(context.getRequestPath());
+ logDO.setRequestQuery(toJson(context.getRequestQueryParams()));
+ logDO.setRequestHeaders(toJson(context.getRequestHeaders()));
+ logDO.setRequestBody(toJson(context.getRequestBody()));
+ logDO.setClientIp(context.getClientIp());
+ logDO.setUserAgent(GatewayHeaderUtils.findFirstHeaderValue(context.getRequestHeaders(), HttpHeaders.USER_AGENT));
+ logDO.setStatus(3);
+ logDO.setRequestTime(toLocalDateTime(context.getRequestTime()));
+ logDO.setTenantId(parseTenantId(context.getTenantId()));
+ return logDO;
}
private Long calculateDuration(ApiInvocationContext context) {
@@ -130,6 +207,14 @@ public class ApiGatewayAccessLogger {
return Duration.between(start, Instant.now()).toMillis();
}
+ private Long calculateDuration(HttpServletRequest request) {
+ Object startAttr = request.getAttribute(ATTR_REQUEST_START);
+ if (startAttr instanceof Instant start) {
+ return Duration.between(start, Instant.now()).toMillis();
+ }
+ return null;
+ }
+
private Integer resolveStatus(Integer httpStatus) {
if (httpStatus == null) {
return 3;
@@ -214,6 +299,19 @@ public class ApiGatewayAccessLogger {
return extra;
}
+ @SafeVarargs
+ private T firstNonNull(T... candidates) {
+ if (candidates == null) {
+ return null;
+ }
+ for (T candidate : candidates) {
+ if (candidate != null) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
private String toJson(Object value) {
if (value == null) {
return null;
@@ -265,27 +363,20 @@ public class ApiGatewayAccessLogger {
}
}
- private String firstNonBlank(String... values) {
- if (values == null) {
- return null;
+ private Map collectHeaders(HttpServletRequest request) {
+ if (request == null) {
+ return Collections.emptyMap();
}
- for (String value : values) {
- if (StringUtils.hasText(value)) {
- return value;
+ Map headers = new LinkedHashMap<>();
+ Enumeration names = request.getHeaderNames();
+ while (names != null && names.hasMoreElements()) {
+ String name = names.nextElement();
+ Enumeration values = request.getHeaders(name);
+ if (values == null || !values.hasMoreElements()) {
+ continue;
}
+ headers.put(name, values.nextElement());
}
- return null;
+ return headers;
}
-
- private Object firstNonNull(Object... values) {
- if (values == null) {
- return null;
- }
- for (Object value : values) {
- if (value != null) {
- return value;
- }
- }
- return null;
- }
-}
+}
\ No newline at end of file
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java
index fadfe510..5d813d52 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayRequestMapper.java
@@ -3,6 +3,7 @@ package com.zt.plat.module.databus.framework.integration.gateway.core;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties;
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext;
+import com.zt.plat.module.databus.framework.integration.gateway.core.ApiGatewayAccessLogger;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
@@ -90,6 +91,7 @@ public class ApiGatewayRequestMapper {
});
context.setUserAgent(GatewayHeaderUtils.findFirstHeaderValue(context.getRequestHeaders(), HttpHeaders.USER_AGENT));
context.setClientIp(resolveClientIp(headers, context.getRequestHeaders()));
+ captureAccessLogId(context);
populateQueryParams(headers, context, originalRequestUri);
if (properties.isEnableTenantHeader()) {
Object tenantHeaderValue = context.getRequestHeaders().get(properties.getTenantHeader());
@@ -114,6 +116,21 @@ public class ApiGatewayRequestMapper {
return context;
}
+ private void captureAccessLogId(ApiInvocationContext context) {
+ String headerValue = GatewayHeaderUtils.findFirstHeaderValue(context.getRequestHeaders(), ApiGatewayAccessLogger.HEADER_ACCESS_LOG_ID);
+ if (!StringUtils.hasText(headerValue)) {
+ return;
+ }
+ try {
+ Long logId = Long.valueOf(headerValue);
+ context.getAttributes().put(ApiGatewayAccessLogger.ATTR_LOG_ID, logId);
+ } catch (NumberFormatException ex) {
+ // 忽略格式问题,仅在属性中保留原文以便排查
+ context.getAttributes().put(ApiGatewayAccessLogger.ATTR_LOG_ID, headerValue);
+ }
+ context.getRequestHeaders().remove(ApiGatewayAccessLogger.HEADER_ACCESS_LOG_ID);
+ }
+
private boolean isInternalHeader(String headerName) {
if (!StringUtils.hasText(headerName)) {
return true;
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java
index 81e19c6a..0c9ad557 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java
@@ -13,6 +13,7 @@ import com.zt.plat.framework.tenant.core.context.TenantContextHolder;
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiClientCredentialDO;
import com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties;
+import com.zt.plat.module.databus.framework.integration.gateway.core.ApiGatewayAccessLogger;
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiGatewayResponse;
import com.zt.plat.module.databus.service.gateway.ApiAnonymousUserService;
import com.zt.plat.module.databus.service.gateway.ApiClientCredentialService;
@@ -56,8 +57,10 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
private final ApiClientCredentialService credentialService;
private final ApiAnonymousUserService anonymousUserService;
private final ObjectMapper objectMapper;
+ private final ApiGatewayAccessLogger accessLogger;
private final AntPathMatcher pathMatcher = new AntPathMatcher();
- private static final TypeReference