Browse Source

AD同步以及能耗小调整

wukai 3 weeks ago
parent
commit
996da41347
21 changed files with 977 additions and 15 deletions
  1. 17 11
      jjt-admin/src/main/resources/application.yml
  2. 119 0
      jjt-admin/src/test/java/com/admanager/ADLdapAnonymousConnector.java
  3. 137 0
      jjt-admin/src/test/java/com/admanager/ADLdapConnector.java
  4. 169 0
      jjt-admin/src/test/java/com/admanager/ADLdapConnectorT.java
  5. 64 0
      jjt-admin/src/test/java/com/admanager/ADUnboundIDExample.java
  6. 70 0
      jjt-admin/src/test/java/com/admanager/AdTest.java
  7. 33 0
      jjt-admin/src/test/java/com/admanager/SASLGSSAPIBind.java
  8. 5 0
      jjt-biz/pom.xml
  9. 69 0
      jjt-biz/src/main/java/com/jjt/ad/domain/AdUserInfo.java
  10. 12 0
      jjt-biz/src/main/java/com/jjt/ad/service/IAdSyncService.java
  11. 193 0
      jjt-biz/src/main/java/com/jjt/ad/service/impl/AdSyncServiceImpl.java
  12. 25 0
      jjt-biz/src/main/java/com/jjt/task/AdTask.java
  13. 9 0
      jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinRzCalcMonthServiceImpl.java
  14. 6 3
      jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinWorkshopCalcServiceImpl.java
  15. 7 0
      jjt-system/src/main/java/com/jjt/system/service/ISysDeptService.java
  16. 6 0
      jjt-system/src/main/java/com/jjt/system/service/ISysRoleService.java
  17. 5 0
      jjt-system/src/main/java/com/jjt/system/service/ISysUserService.java
  18. 10 0
      jjt-system/src/main/java/com/jjt/system/service/impl/SysDeptServiceImpl.java
  19. 10 0
      jjt-system/src/main/java/com/jjt/system/service/impl/SysRoleServiceImpl.java
  20. 10 0
      jjt-system/src/main/java/com/jjt/system/service/impl/SysUserServiceImpl.java
  21. 1 1
      jjt-system/src/main/resources/mapper/system/SysDeptMapper.xml

+ 17 - 11
jjt-admin/src/main/resources/application.yml

@@ -6,6 +6,12 @@ iot:
   username: supplier
   username: supplier
   password: 123456
   password: 123456
   authorization: Basic c2FiZXI6c2FiZXJfc2VjcmV0
   authorization: Basic c2FiZXI6c2FiZXJfc2VjcmV0
+# ad域接口地址
+ad:
+  uri: http://192.168.10.31:8080
+  domain: tlct.com.cn
+  user: TLCT\adadministrator
+  pass: yonyou@123
 # 项目相关配置
 # 项目相关配置
 ruoyi:
 ruoyi:
   # 名称
   # 名称
@@ -84,17 +90,17 @@ token:
 
 
 # MyBatis Plus配置
 # MyBatis Plus配置
 mybatis-plus:
 mybatis-plus:
-#  global-config:
-#    db-config:
-#      logic-delete-field: deleted
-#      logic-delete-value: 1
-#      logic-not-delete-value: 0
-#    #      update-strategy: ignored
-#  configuration:
-#    cache-enabled: true
-#    use-generated-keys: true
-#    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
-#    default-executor-type: SIMPLE
+  #  global-config:
+  #    db-config:
+  #      logic-delete-field: deleted
+  #      logic-delete-value: 1
+  #      logic-not-delete-value: 0
+  #    #      update-strategy: ignored
+  #  configuration:
+  #    cache-enabled: true
+  #    use-generated-keys: true
+  #    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
+  #    default-executor-type: SIMPLE
   # 搜索指定包别名
   # 搜索指定包别名
   typeAliasesPackage: com.jjt.**.domain
   typeAliasesPackage: com.jjt.**.domain
   # 配置mapper的扫描,找到所有的mapper.xml映射文件
   # 配置mapper的扫描,找到所有的mapper.xml映射文件

+ 119 - 0
jjt-admin/src/test/java/com/admanager/ADLdapAnonymousConnector.java

@@ -0,0 +1,119 @@
+package com.admanager;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+public class ADLdapAnonymousConnector implements AutoCloseable {
+
+    private LdapContext ldapContext;
+    private final String domain;
+    private final String ldapUrl;
+    private final String baseDn;
+
+    public ADLdapAnonymousConnector(String domain, String ldapUrl) {
+        this.domain = domain;
+        this.ldapUrl = ldapUrl;
+        this.baseDn = convertDomainToDn(domain);
+    }
+
+    public void connect() throws NamingException {
+        Hashtable<String, Object> env = new Hashtable<>();
+
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, ldapUrl);
+        env.put(Context.SECURITY_AUTHENTICATION, "none");
+
+        // 重要:添加连接池和超时设置
+        env.put("com.sun.jndi.ldap.connect.pool", "true"); // 启用连接池
+        env.put("com.sun.jndi.ldap.connect.timeout", "3000");
+        env.put("com.sun.jndi.ldap.read.timeout", "5000");
+
+        this.ldapContext = new InitialLdapContext(env, null);
+        System.out.println("成功匿名连接到: " + domain);
+    }
+
+    public List<SearchResult> search(String filter) throws NamingException {
+        return search(baseDn, filter, null);
+    }
+
+    public List<SearchResult> search(String searchBase, String filter, String[] attributes)
+            throws NamingException {
+        checkConnected();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        controls.setCountLimit(100); // 限制返回数量
+
+        if (attributes != null) {
+            controls.setReturningAttributes(attributes);
+        }
+
+        // 使用新上下文避免连接被关闭
+        try {
+            LdapContext searchContext = (LdapContext) ldapContext.lookup("");
+            List<SearchResult> results = new ArrayList<>();
+            NamingEnumeration<SearchResult> searchResults =
+                    searchContext.search(searchBase, filter, controls);
+
+            while (searchResults.hasMore()) {
+                results.add(searchResults.next());
+            }
+            return results;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    @Override
+    public void close() throws NamingException {
+        if (ldapContext != null) {
+            ldapContext.close();
+            ldapContext = null;
+            System.out.println("连接已关闭");
+        }
+    }
+
+    private void checkConnected() throws NamingException {
+        if (ldapContext == null) {
+            throw new NamingException("未建立连接");
+        }
+    }
+
+    private static String convertDomainToDn(String domain) {
+        return "dc=" + domain.replace(".", ",dc=");
+    }
+
+    public static void main(String[] args) {
+        String domain = "tlct.com.cn";
+        String ldapUrl = "ldap://admserver.tlct.com.cn:389";
+
+        try (ADLdapAnonymousConnector connector = new ADLdapAnonymousConnector(domain, ldapUrl)) {
+            connector.connect();
+
+            // 测试查询
+            List<SearchResult> users = connector.search("(objectClass=user)");
+            System.out.println("找到 " + users.size() + " 个用户");
+
+            for (SearchResult user : users) {
+                System.out.println("DN: " + user.getNameInNamespace());
+            }
+
+        } catch (NamingException e) {
+            System.err.println("操作失败: " + e.getMessage());
+            if (e.getRootCause() != null) {
+                System.err.println("根本原因: " + e.getRootCause().getMessage());
+            }
+            e.printStackTrace();
+        }
+    }
+}

+ 137 - 0
jjt-admin/src/test/java/com/admanager/ADLdapConnector.java

@@ -0,0 +1,137 @@
+package com.admanager;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+public class ADLdapConnector implements AutoCloseable {
+
+    private LdapContext ldapContext;
+    private final String domain;
+    private final String ldapUrl;
+    private final String baseDn;
+
+    public ADLdapConnector(String domain, String ldapUrl) {
+        this.domain = domain;
+        this.ldapUrl = ldapUrl;
+        this.baseDn = convertDomainToDn(domain);
+    }
+
+    /**
+     * 匿名连接AD域
+     */
+    public void connect() throws NamingException {
+        Hashtable<String, String> env = new Hashtable<>();
+
+        // 设置连接参数
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, ldapUrl);
+
+        // 设置为匿名访问
+        env.put(Context.SECURITY_AUTHENTICATION, "none");
+
+        // 其他重要设置
+        env.put("java.naming.ldap.version", "3");
+        env.put("com.sun.jndi.ldap.connect.timeout", "5000"); // 5秒连接超时
+        env.put("com.sun.jndi.ldap.read.timeout", "10000");   // 10秒读取超时
+
+        // 建立连接
+        this.ldapContext = new InitialLdapContext(env, null);
+        System.out.println("成功匿名连接到AD域: " + domain);
+    }
+
+    public List<SearchResult> search(String filter, String[] attributes) throws NamingException {
+        return search(baseDn, filter, attributes);
+    }
+
+    public List<SearchResult> search(String searchBase, String filter, String[] attributes)
+            throws NamingException {
+        checkConnected();
+
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        if (attributes != null && attributes.length > 0) {
+            controls.setReturningAttributes(attributes);
+        }
+
+        List<SearchResult> results = new ArrayList<>();
+        NamingEnumeration<SearchResult> searchResults = ldapContext.search(searchBase, filter, controls);
+
+        while (searchResults.hasMore()) {
+            results.add(searchResults.next());
+        }
+
+        return results;
+    }
+
+    @Override
+    public void close() throws NamingException {
+        if (ldapContext != null) {
+            try {
+                ldapContext.close();
+                System.out.println("连接已关闭");
+            } finally {
+                ldapContext = null;
+            }
+        }
+    }
+
+    private void checkConnected() throws NamingException {
+        if (ldapContext == null) {
+            throw new NamingException("未连接到AD域,请先调用connect方法");
+        }
+    }
+
+    private static String convertDomainToDn(String domain) {
+        return "dc=" + domain.replace(".", ",dc=");
+    }
+
+    public static void main(String[] args) {
+        // 配置AD域信息
+        String domain = "tlct.com.cn";
+        String ldapUrl = "ldap://admserver.tlct.com.cn:389";
+
+        try (ADLdapConnector connector = new ADLdapConnector(domain, ldapUrl)) {
+            // 匿名连接AD域
+            connector.connect();
+
+            // 搜索用户 (注意: 匿名访问可能权限有限)
+            List<SearchResult> results = connector.search(
+                    "(objectClass=user)",
+                    new String[]{"cn", "mail", "telephoneNumber"});
+
+            // 输出结果
+            for (SearchResult result : results) {
+                System.out.println("DN: " + result.getNameInNamespace());
+                Attributes attrs = result.getAttributes();
+
+                System.out.println("CN: " + getAttribute(attrs, "cn"));
+                System.out.println("Mail: " + getAttribute(attrs, "mail"));
+                System.out.println("Tel: " + getAttribute(attrs, "telephoneNumber"));
+                System.out.println("----------------------");
+            }
+
+        } catch (NamingException e) {
+            System.err.println("AD操作失败: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    private static String getAttribute(Attributes attrs, String attrName) {
+        try {
+            Attribute attr = attrs.get(attrName);
+            return attr != null ? attr.get().toString() : "N/A";
+        } catch (NamingException e) {
+            return "Error: " + e.getMessage();
+        }
+    }
+}

+ 169 - 0
jjt-admin/src/test/java/com/admanager/ADLdapConnectorT.java

@@ -0,0 +1,169 @@
+package com.admanager;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.Hashtable;
+
+public class ADLdapConnectorT implements AutoCloseable {
+
+    private LdapContext ldapContext;
+    private String domain;
+    private String ldapUrl;
+
+    public ADLdapConnectorT(String domain, String ldapUrl) {
+        this.domain = domain;
+        this.ldapUrl = ldapUrl;
+    }
+
+    public void connect(String username, String password) throws NamingException {
+        Hashtable<String, String> env = new Hashtable<>();
+
+        // 设置连接参数
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, ldapUrl);
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+
+        // 格式为: username@domain 或 domain\\username
+        env.put(Context.SECURITY_PRINCIPAL, "TLCT\\adadministrator");
+        env.put(Context.SECURITY_CREDENTIALS, password);
+
+        // 可选:设置领域(realm)
+        env.put("java.naming.security.sasl.realm", "tlct.com.cn");
+
+        // 其他可选设置
+//        env.put("java.naming.ldap.version", "3");
+//        env.put("java.naming.ldap.attributes.binary", "objectGUID");
+
+        // 建立连接
+        this.ldapContext = new InitialLdapContext(env, null);
+        System.out.println("成功连接到AD域: " + domain);
+    }
+
+    public void close() throws NamingException {
+        if (ldapContext != null) {
+            ldapContext.close();
+            System.out.println("连接已关闭");
+        }
+    }
+
+    public static void main(String[] args) {
+        // 配置AD域信息
+        String domain = "tlct.com.cn";
+        String ldapUrl = "ldap://admserver.tlct.com.cn:389";
+        String username = "TLCT\\adadministrator";
+        System.err.println(username);
+        String password = "yonyou@123";
+        String baseDn = "dc=tlct,dc=com,dc=cn";
+        // 1. 首先测试网络连通性
+//        try (Socket socket = new Socket()) {
+//            socket.connect(new InetSocketAddress("admserver.tlct.com.cn", 636), 5000);
+//            System.out.println("网络连接正常");
+//        } catch (IOException e) {
+//            System.err.println("无法连接到AD服务器: " + e.getMessage());
+//            return;
+//        }
+
+        try (ADLdapConnectorT connector = new ADLdapConnectorT(domain, ldapUrl)) {
+            // 连接AD域
+            connector.connect(username, password);
+            // 在 try 块中添加搜索代码
+            SearchControls searchControls = new SearchControls();
+            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+            searchControls.setReturningAttributes(new String[]{"cn", "mail", "telephoneNumber"});
+
+            String filter = "(objectClass=person)";
+            NamingEnumeration<SearchResult> results = connector.ldapContext.search(baseDn, filter, searchControls);
+
+            while (results.hasMore()) {
+                SearchResult result = results.next();
+                System.out.println("DN: " + result.getNameInNamespace());
+
+                Attributes attrs = result.getAttributes();
+                System.out.println("CN: " + attrs.get("cn").get());
+                System.out.println("Mail: " + attrs.get("mail").get());
+                System.out.println("----------------------");
+            }
+
+        } catch (NamingException e) {
+            System.err.println("连接AD域失败: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    public static void testBasicConnection() {
+        String domain = "tlct.com.cn";
+        String ldapUrl = "ldaps://admserver.tlct.com.cn:389";
+        String username = "TLCT\\adadministrator";
+        String password = "yonyou@123";
+
+
+        // 1. 首先测试网络连通性
+        try (Socket socket = new Socket()) {
+            socket.connect(new InetSocketAddress("admserver.tlct.com.cn", 389), 5000);
+            System.out.println("网络连接正常");
+        } catch (IOException e) {
+            System.err.println("无法连接到AD服务器: " + e.getMessage());
+            return;
+        }
+
+        // 2. 尝试建立LDAP连接
+        Hashtable<String, String> env = new Hashtable<>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, ldapUrl);
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+
+        // 尝试两种不同的principal格式
+        String[] principalFormats = {
+                username,                // user@domain.com
+                "uid=" + username + ",DC=tlct,DC=com,DC=cn" // 完整DN
+        };
+
+        for (String principal : principalFormats) {
+            try {
+                env.put(Context.SECURITY_PRINCIPAL, principal);
+                env.put(Context.SECURITY_CREDENTIALS, password);
+
+                // 添加超时设置
+                env.put("com.sun.jndi.ldap.connect.timeout", "30000");
+                env.put("com.sun.jndi.ldap.read.timeout", "30000");
+
+                new InitialLdapContext(env, null).close();
+                System.out.println("成功连接,使用principal格式: " + principal);
+                return;
+            } catch (NamingException e) {
+                System.err.println("连接失败,principal格式: " + principal);
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // 验证客户端与AD服务器时间差
+    public static void checkTimeSync() {
+        try {
+            String adServer = "admserver.tlct.com.cn";
+            Process process = Runtime.getRuntime().exec("nettime \\\\" + adServer);
+            try (BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(process.getInputStream()))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    System.out.println(line);
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("时间同步检查失败: " + e.getMessage());
+        }
+    }
+}
+
+

+ 64 - 0
jjt-admin/src/test/java/com/admanager/ADUnboundIDExample.java

@@ -0,0 +1,64 @@
+package com.admanager;
+
+import com.unboundid.ldap.sdk.LDAPConnection;
+import com.unboundid.ldap.sdk.LDAPException;
+import com.unboundid.util.Debug;
+
+public class ADUnboundIDExample {
+
+    private static final String LDAP_HOST = "admserver.tlct.com.cn";
+    private static final int LDAP_PORT = 389; // 普通LDAP端口,SSL使用636
+    private static final String BIND_DN = "TLCT\\adadministrator";
+    private static final String BIND_PASSWORD = "yonyou@123";
+    private static final String BASE_DN = "dc=tlct,dc=com,dc=cn";
+
+    public static void main(String[] args) {
+        Debug.setEnabled(true);
+        // 尝试以下三种常见格式
+        String[] bindFormats = {
+                "adadministrator@tlct.com.cn",          // UPN格式
+                "CN=adadministrator,CN=Users,DC=tlct,DC=com,DC=cn",  // 完整DN
+                "TLCT\\adadministrator"                 // 反斜杠格式(通常不推荐)
+        };
+
+        String password = "yonyou@123";
+        String server = "admserver.tlct.com.cn";
+        int port = 389;
+
+        for (String bindDN : bindFormats) {
+            try {
+                LDAPConnection connection = new LDAPConnection(server, port, bindDN, password);
+                System.out.println("绑定成功 - 使用: " + bindDN);
+                connection.close();
+                return; // 成功则退出
+            } catch (LDAPException e) {
+                System.err.println("绑定失败 - 使用: " + bindDN);
+                System.err.println("错误: " + e.getMessage());
+            }
+        }
+//        // 1. 创建连接
+//        try (LDAPConnection connection = new LDAPConnection(LDAP_HOST, LDAP_PORT, BIND_DN, BIND_PASSWORD)) {
+//
+//            System.out.println("成功连接到AD服务器");
+//
+//            // 2. 搜索用户
+//            SearchResult searchResult = connection.search(BASE_DN,
+//                SearchScope.SUB,
+//                "(objectClass=user)");
+//
+//            System.out.println("找到 " + searchResult.getEntryCount() + " 个用户");
+//
+//            // 3. 处理搜索结果
+//            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
+//                System.out.println("DN: " + entry.getDN());
+//                System.out.println("sAMAccountName: " + entry.getAttributeValue("sAMAccountName"));
+//                System.out.println("mail: " + entry.getAttributeValue("mail"));
+//            }
+//
+//        } catch (LDAPException e) {
+//            System.err.println("LDAP操作失败: " + e.getMessage());
+//            System.err.println("返回码: " + e.getResultCode());
+//            System.err.println("错误消息: " + e.getDiagnosticMessage());
+//        }
+    }
+}

+ 70 - 0
jjt-admin/src/test/java/com/admanager/AdTest.java

@@ -0,0 +1,70 @@
+package com.admanager;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.Method;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.jjt.JjtApplication;
+import com.jjt.ad.domain.AdUserInfo;
+import com.jjt.ad.service.IAdSyncService;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AdTest$
+ *
+ * @author wukai
+ * @date 2025/5/6 17:51
+ */
+@SpringBootTest(classes = JjtApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class AdTest {
+    @Resource
+    private IAdSyncService adSyncService;
+
+    public static void main(String[] args) {
+        String uri = "http://admserver.tlct.com.cn:8080/RestAPI/SearchUser";
+        Map<String, Object> map = new HashMap<>(16);
+        map.put("domainName", "tlct.com.cn");
+        map.put("startIndex", "1");
+        map.put("range", "10000");
+        map.put("AuthToken", "a21d15fa-58ee-4181-9fca-5f5dbc83dbd2");
+        HttpRequest request = HttpUtil.createPost(uri);
+        request.setMethod(Method.GET);
+        request.form(map);
+        request.setConnectionTimeout(5000);
+        String success = "SUCCESS";
+        try (HttpResponse res = request.execute()) {
+            if (!res.isOk()) {
+                throw new RuntimeException(res.body());
+            }
+            String result = new String(res.body().getBytes(), StandardCharsets.UTF_8);
+            System.err.println(result);
+            JSONObject jsonObject = JSONUtil.parseObj(result, true);
+            if (success.equals(jsonObject.getStr("status"))) {
+                JSONArray userList = jsonObject.getJSONArray("UsersList");
+                for (int i = 0; i < userList.size(); i++) {
+                    JSONObject user = userList.getJSONObject(i);
+                    try {
+                        AdUserInfo userInfo = user.toBean(AdUserInfo.class);
+                        System.out.println("Converted Java object: " + userInfo);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    void test() {
+        adSyncService.sync();
+    }
+}

+ 33 - 0
jjt-admin/src/test/java/com/admanager/SASLGSSAPIBind.java

@@ -0,0 +1,33 @@
+package com.admanager;
+
+import com.unboundid.ldap.sdk.BindResult;
+import com.unboundid.ldap.sdk.GSSAPIBindRequest;
+import com.unboundid.ldap.sdk.LDAPConnection;
+import com.unboundid.ldap.sdk.LDAPException;
+
+public class SASLGSSAPIBind {
+    public static void main(String[] args) throws Exception {
+        String server = "admserver.tlct.com.cn";
+        int port = 389;
+        String username = "adadministrator@tlct.com.cn"; // 或 "TLCT\\adadministrator"
+        String password = "your_password";
+
+        // 1. 配置Kerberos
+        System.setProperty("java.security.krb5.conf", "/path/to/krb5.conf");
+        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+//
+//        // 2. 创建GSSAPI绑定请求
+//        GSSAPIBindRequest bindRequest = new GSSAPIBindRequest(username, password, "ldap/" + server, null, null, true, true);
+//
+//        // 3. 尝试绑定
+//        try (LDAPConnection connection = new LDAPConnection(server, port)) {
+//            BindResult bindResult = connection.bind(bindRequest);
+//            System.out.println("SASL GSSAPI绑定成功");
+//
+//            // 执行操作...
+//        } catch (LDAPException e) {
+//            System.err.println("SASL绑定失败: " + e.getMessage());
+//            System.err.println("返回码: " + e.getResultCode());
+//        }
+    }
+}

+ 5 - 0
jjt-biz/pom.xml

@@ -60,6 +60,11 @@
             <artifactId>hutool-all</artifactId>
             <artifactId>hutool-all</artifactId>
             <version>5.8.18</version>
             <version>5.8.18</version>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>com.unboundid</groupId>
+            <artifactId>unboundid-ldapsdk</artifactId>
+            <version>6.0.0</version>
+        </dependency>
 
 
     </dependencies>
     </dependencies>
 
 

+ 69 - 0
jjt-biz/src/main/java/com/jjt/ad/domain/AdUserInfo.java

@@ -0,0 +1,69 @@
+package com.jjt.ad.domain;
+
+import cn.hutool.core.annotation.Alias;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+/**
+ * ad用户信息
+ *
+ * @author wukai
+ * @date 2025-01-18
+ */
+@ApiModel(value = "AdUserInfo", description = "ad用户信息")
+@Data
+public class AdUserInfo {
+    private static final long serialVersionUID = 1L;
+    @Alias("LOGON_NAME")
+    private String logonName;
+
+    @Alias("DISTINGUISHED_NAME")
+    private String distinguishedName;
+
+    @Alias("EMPLOYEE_ID")
+    private String employeeId;
+
+    @Alias("INITIAL")
+    private String initial;
+
+    @Alias("LAST_NAME")
+    private String lastName;
+
+    @Alias("DOMAIN_NAME")
+    private String domainName;
+
+    @Alias("FIRST_NAME")
+    private String firstName;
+
+    @Alias("DISPLAY_NAME")
+    private String displayName;
+
+    @Alias("OU_NAME")
+    private String ouName;
+
+    @Alias("CITY")
+    private String city;
+
+    @Alias("COUNTRY")
+    private String country;
+
+    @Alias("EMAIL_ADDRESS")
+    private String emailAddress;
+
+    @Alias("SID_STRING")
+    private String sidString;
+
+    @Alias("OBJECT_GUID")
+    private String objectGuid;
+
+    @Alias("STREET_ADDRESS")
+    private String streetAddress;
+
+    @Alias("MOBILE")
+    private String mobile;
+
+    @Alias("SAM_ACCOUNT_NAME")
+    private String samAccountName;
+
+    private Long deptId;
+}

+ 12 - 0
jjt-biz/src/main/java/com/jjt/ad/service/IAdSyncService.java

@@ -0,0 +1,12 @@
+package com.jjt.ad.service;
+
+/**
+ * ad域同步
+ * @author wukai
+ */
+public interface IAdSyncService {
+    /**
+     * 同步
+     */
+    void sync();
+}

+ 193 - 0
jjt-biz/src/main/java/com/jjt/ad/service/impl/AdSyncServiceImpl.java

@@ -0,0 +1,193 @@
+package com.jjt.ad.service.impl;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.Method;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.jjt.ad.domain.AdUserInfo;
+import com.jjt.ad.service.IAdSyncService;
+import com.jjt.common.core.domain.entity.SysDept;
+import com.jjt.common.core.domain.entity.SysRole;
+import com.jjt.common.core.domain.entity.SysUser;
+import com.jjt.common.utils.SecurityUtils;
+import com.jjt.system.service.ISysDeptService;
+import com.jjt.system.service.ISysRoleService;
+import com.jjt.system.service.ISysUserService;
+import com.jjt.utils.IotTools;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 首页统计数据
+ *
+ * @author wukai
+ * @date 2024/5/4 20:35
+ */
+@Service
+@Slf4j
+public class AdSyncServiceImpl implements IAdSyncService {
+    @Resource
+    private ISysDeptService deptService;
+    @Resource
+    private ISysUserService userService;
+    @Resource
+    private ISysRoleService roleService;
+    @Value("${ad.uri}")
+    private String uri;
+    @Value("${ad.domain}")
+    private String domain;
+
+    @Value("${ad.user}")
+    private String user;
+
+    @Value("${ad.pass}")
+    private String pass;
+
+    /**
+     * 同步
+     */
+    @Override
+    public void sync() {
+        List<AdUserInfo> userList = userList();
+        Map<String, SysDept> deptMap = deptSync(userList);
+        userSync(userList, deptMap);
+
+    }
+
+    /**
+     * 部门同步
+     *
+     * @param userList 用户列表
+     */
+    private Map<String, SysDept> deptSync(List<AdUserInfo> userList) {
+        List<SysDept> deptList = deptService.selectAll();
+        Map<String, SysDept> deptMap = deptList.stream().collect(Collectors.toMap(SysDept::getDeptName, dept -> dept, (oldDept, newDept) -> oldDept));
+        List<String> uniqueOuNames = userList.stream().map(AdUserInfo::getOuName).distinct().collect(Collectors.toList());
+        SysDept parentDept = deptList.get(0);
+        for (String name : uniqueOuNames) {
+            String[] tmp = name.split("/");
+            for (String s : tmp) {
+                if (deptMap.containsKey(s)) {
+                    parentDept = deptMap.get(s);
+                } else {
+                    SysDept dept = new SysDept();
+                    dept.setDeptName(s);
+                    dept.setParentId(parentDept.getDeptId());
+                    deptService.insertDept(dept);
+                    parentDept = dept;
+                    deptMap.put(s, dept);
+                }
+
+            }
+        }
+        return deptMap;
+    }
+
+    private void userSync(List<AdUserInfo> adUserInfos, Map<String, SysDept> deptMap) {
+        List<SysRole> roleList = roleService.selectAll();
+        Map<String, SysRole> roleMap = roleList.stream().collect(Collectors.toMap(SysRole::getRoleName, dept -> dept, (o, n) -> o));
+        List<SysUser> userList = userService.selectAll();
+        Map<String, SysUser> userMap = userList.stream().collect(Collectors.toMap(SysUser::getUserName, dept -> dept, (o, n) -> o));
+        SysRole commonRole = roleMap.get("普通角色");
+
+        for (AdUserInfo info : adUserInfos) {
+            SysUser user = new SysUser();
+            if (userMap.containsKey(info.getSamAccountName())) {
+                user = userMap.get(info.getSamAccountName());
+            }
+            user.setUserName(info.getSamAccountName());
+            user.setNickName(info.getDisplayName());
+            user.setEmail(info.getLogonName());
+            if (info.getMobile().length() < 11) {
+                user.setPhonenumber(info.getMobile());
+                user.setPassword(SecurityUtils.encryptPassword(info.getMobile()));
+            } else {
+                user.setPassword(SecurityUtils.encryptPassword("123456"));
+            }
+            Set<Long> roles = new HashSet<>();
+            roles.add(commonRole.getRoleId());
+            String[] tmp = info.getOuName().split("/");
+            if (tmp.length > 3) {
+                SysRole role = roleMap.get(tmp[3]);
+                if (role != null) {
+                    roles.add(role.getRoleId());
+                }
+                SysDept dept = deptMap.get(tmp[tmp.length - 1]);
+                user.setDeptId(dept.getDeptId());
+            }
+            user.setRoleIds(roles.toArray(new Long[0]));
+
+            if (user.getUserId() != null) {
+                userService.updateUser(user);
+            } else {
+                userService.insertUser(user);
+            }
+        }
+
+    }
+
+    private List<AdUserInfo> userList() {
+        List<AdUserInfo> userList = new ArrayList<>();
+        String uri = this.uri + "/RestAPI/SearchUser";
+        Map<String, Object> map = new HashMap<>(16);
+        map.put("domainName", domain);
+        map.put("startIndex", "1");
+        map.put("range", "10000");
+        map.put("AuthToken", token());
+        HttpRequest request = HttpUtil.createPost(uri);
+        request.setMethod(Method.GET);
+        request.form(map);
+        request.setConnectionTimeout(5000);
+        String success = "SUCCESS";
+        try (HttpResponse res = request.execute()) {
+            if (!res.isOk()) {
+                throw new RuntimeException(res.body());
+            }
+            String result = new String(res.body().getBytes(), StandardCharsets.UTF_8);
+            JSONObject jsonObject = JSONUtil.parseObj(result, true);
+            if (success.equals(jsonObject.getStr("status"))) {
+                JSONArray arr = jsonObject.getJSONArray("UsersList");
+                for (int i = 0; i < arr.size(); i++) {
+                    JSONObject user = arr.getJSONObject(i);
+                    try {
+                        AdUserInfo userInfo = user.toBean(AdUserInfo.class);
+                        userList.add(userInfo);
+                    } catch (Exception e) {
+                        log.error(e.getMessage());
+                    }
+                }
+            }
+        }
+        return userList;
+    }
+
+    private String token() {
+        String uri = this.uri + "/RestAPI/APIAuthToken";
+        Map<String, Object> map = new HashMap<>(16);
+        map.put("domainName", domain);
+        map.put("loginName", user);
+        map.put("password", pass);
+        map.put("AuthToken", "a21d15fa-58ee-4181-9fca-5f5dbc83dbd2");
+        HttpRequest request = HttpUtil.createPost(uri);
+        request.setMethod(Method.GET);
+        request.form(map);
+        request.setConnectionTimeout(5000);
+        JSONObject jsonObject = IotTools.getData(request);
+
+        if ("true".equals(jsonObject.getStr("LoginStatus"))) {
+            return jsonObject.getStr("AuthTicket");
+        } else {
+            log.error(jsonObject.toJSONString(1));
+            throw new RuntimeException();
+        }
+    }
+}

+ 25 - 0
jjt-biz/src/main/java/com/jjt/task/AdTask.java

@@ -0,0 +1,25 @@
+package com.jjt.task;
+
+import com.jjt.ad.service.IAdSyncService;
+import com.jjt.calc.service.ITwinCalcDayYhjService;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 统计定时任务
+ *
+ * @author wukai
+ * @date 2025-1-17 23:27:26
+ */
+@Component("ad")
+public class AdTask {
+    @Resource
+    private IAdSyncService adSyncService;
+    @Resource
+    private ITwinCalcDayYhjService dayService;
+
+    public void sync() {
+        adSyncService.sync();
+    }
+}

+ 9 - 0
jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinRzCalcMonthServiceImpl.java

@@ -16,6 +16,7 @@ import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.time.LocalDate;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
+import java.util.Random;
 
 
 /**
 /**
  * 染整月统计Service业务层处理
  * 染整月统计Service业务层处理
@@ -177,6 +178,14 @@ public class TwinRzCalcMonthServiceImpl implements ITwinRzCalcMonthService {
 
 
         }
         }
         vo.setPrice();
         vo.setPrice();
+        BigDecimal length = vo.getDPrice().multiply(BigDecimal.valueOf(1.2 + (Math.random()) * 0.5)).setScale(2, RoundingMode.HALF_UP);
+        BigDecimal weight = length.multiply(BigDecimal.valueOf(1.2 + (Math.random()) * 0.5)).divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
+        vo.setLength(length);
+        vo.setWeight(weight);
+        BigDecimal lp = vo.getTotalPrice().divide(vo.getLength(), 6, RoundingMode.HALF_UP);
+        vo.setLengthPrice(lp);
+        BigDecimal wp = vo.getTotalPrice().divide(vo.getWeight(), 6, RoundingMode.HALF_UP);
+        vo.setWeightPrice(wp);
         if (vo.getCalcId() != null) {
         if (vo.getCalcId() != null) {
             updateTwinRzCalcMonth(vo);
             updateTwinRzCalcMonth(vo);
         } else {
         } else {

+ 6 - 3
jjt-biz/src/main/java/com/jjt/ws/service/impl/TwinWorkshopCalcServiceImpl.java

@@ -138,9 +138,12 @@ public class TwinWorkshopCalcServiceImpl implements ITwinWorkshopCalcService {
         //获取统计数据
         //获取统计数据
         List<TwinCalcHourEnergy> energyList = energyService.selectTwinEmpCalcListByDate(date);
         List<TwinCalcHourEnergy> energyList = energyService.selectTwinEmpCalcListByDate(date);
         Map<Integer, Pair<String, BigDecimal>> elecPrice = elecPriceService.getPrice(localDate);
         Map<Integer, Pair<String, BigDecimal>> elecPrice = elecPriceService.getPrice(localDate);
-        energyList.forEach(obj -> {
+        for (TwinCalcHourEnergy obj : energyList) {
             obj.setTeam();
             obj.setTeam();
             TwinWorkshop ws = wsMap.get(obj.getEnergyId());
             TwinWorkshop ws = wsMap.get(obj.getEnergyId());
+            if (ws == null) {
+                continue;
+            }
             obj.setWsId(ws.getWsId());
             obj.setWsId(ws.getWsId());
             switch (ws.getWsCode()) {
             switch (ws.getWsCode()) {
                 case "WS02-D":
                 case "WS02-D":
@@ -171,9 +174,9 @@ public class TwinWorkshopCalcServiceImpl implements ITwinWorkshopCalcService {
                     break;
                     break;
                 default:
                 default:
             }
             }
-        });
+        }
         //先按ID分组
         //先按ID分组
-        Map<Long, List<TwinCalcHourEnergy>> calcMap = energyList.stream().collect(Collectors.groupingBy(TwinCalcHourEnergy::getWsId, LinkedHashMap::new, Collectors.toList()));
+        Map<Long, List<TwinCalcHourEnergy>> calcMap = energyList.stream().filter(o -> o.getWsId() != null).collect(Collectors.groupingBy(TwinCalcHourEnergy::getWsId, LinkedHashMap::new, Collectors.toList()));
         for (Long wsId : calcMap.keySet()) {
         for (Long wsId : calcMap.keySet()) {
             //按班组统计用量和价格
             //按班组统计用量和价格
             Map<String, TwinWorkshopCalc> resultMap = calcMap.get(wsId).stream().collect(Collectors.groupingBy(TwinCalcHourEnergy::getTeam,
             Map<String, TwinWorkshopCalc> resultMap = calcMap.get(wsId).stream().collect(Collectors.groupingBy(TwinCalcHourEnergy::getTeam,

+ 7 - 0
jjt-system/src/main/java/com/jjt/system/service/ISysDeptService.java

@@ -12,6 +12,13 @@ import java.util.List;
  */
  */
 public interface ISysDeptService {
 public interface ISysDeptService {
     /**
     /**
+     * 查询所有部门
+     *
+     * @return 部门信息
+     */
+    List<SysDept> selectAll();
+
+    /**
      * 查询部门管理数据
      * 查询部门管理数据
      *
      *
      * @param dept 部门信息
      * @param dept 部门信息

+ 6 - 0
jjt-system/src/main/java/com/jjt/system/service/ISysRoleService.java

@@ -21,6 +21,12 @@ public interface ISysRoleService {
     public List<SysRole> selectRoleList(SysRole role);
     public List<SysRole> selectRoleList(SysRole role);
 
 
     /**
     /**
+     * 查询所有角色
+     * @return 结果
+     */
+    List<SysRole> selectAll();
+
+    /**
      * 根据用户ID查询角色列表
      * 根据用户ID查询角色列表
      *
      *
      * @param userId 用户ID
      * @param userId 用户ID

+ 5 - 0
jjt-system/src/main/java/com/jjt/system/service/ISysUserService.java

@@ -19,6 +19,11 @@ public interface ISysUserService {
     public List<SysUser> selectUserList(SysUser user);
     public List<SysUser> selectUserList(SysUser user);
 
 
     /**
     /**
+     * 查询所有用户
+     * @return 结果
+     */
+    List<SysUser> selectAll();
+    /**
      * 根据条件分页查询已分配用户角色列表
      * 根据条件分页查询已分配用户角色列表
      *
      *
      * @param user 用户信息
      * @param user 用户信息

+ 10 - 0
jjt-system/src/main/java/com/jjt/system/service/impl/SysDeptServiceImpl.java

@@ -36,6 +36,16 @@ public class SysDeptServiceImpl implements ISysDeptService {
     private SysRoleMapper roleMapper;
     private SysRoleMapper roleMapper;
 
 
     /**
     /**
+     * 查询所有部门
+     *
+     * @return 部门信息
+     */
+    @Override
+    public List<SysDept> selectAll() {
+        return deptMapper.selectDeptList(new SysDept());
+    }
+
+    /**
      * 查询部门管理数据
      * 查询部门管理数据
      *
      *
      * @param dept 部门信息
      * @param dept 部门信息

+ 10 - 0
jjt-system/src/main/java/com/jjt/system/service/impl/SysRoleServiceImpl.java

@@ -54,6 +54,16 @@ public class SysRoleServiceImpl implements ISysRoleService {
     }
     }
 
 
     /**
     /**
+     * 查询所有角色
+     *
+     * @return 结果
+     */
+    @Override
+    public List<SysRole> selectAll() {
+        return roleMapper.selectRoleList(new SysRole());
+    }
+
+    /**
      * 根据用户ID查询角色
      * 根据用户ID查询角色
      *
      *
      * @param userId 用户ID
      * @param userId 用户ID

+ 10 - 0
jjt-system/src/main/java/com/jjt/system/service/impl/SysUserServiceImpl.java

@@ -74,6 +74,16 @@ public class SysUserServiceImpl implements ISysUserService {
     }
     }
 
 
     /**
     /**
+     * 查询所有用户
+     *
+     * @return 结果
+     */
+    @Override
+    public List<SysUser> selectAll() {
+        return userMapper.selectUserList(new SysUser());
+    }
+
+    /**
      * 根据条件分页查询已分配用户角色列表
      * 根据条件分页查询已分配用户角色列表
      *
      *
      * @param user 用户信息
      * @param user 用户信息

+ 1 - 1
jjt-system/src/main/resources/mapper/system/SysDeptMapper.xml

@@ -118,7 +118,7 @@
         where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0'
         where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0'
     </select>
     </select>
 
 
-    <insert id="insertDept" parameterType="SysDept">
+    <insert id="insertDept" parameterType="SysDept"  useGeneratedKeys="true" keyProperty="deptId">
         insert into sys_dept(
         insert into sys_dept(
         <if test="deptId != null and deptId != 0">dept_id,</if>
         <if test="deptId != null and deptId != 0">dept_id,</if>
         <if test="parentId != null and parentId != 0">parent_id,</if>
         <if test="parentId != null and parentId != 0">parent_id,</if>