Browse Source

Merge remote-tracking branch 'origin/dev' into dev

dev-V1.0.1
庄锐波 6 months ago
parent
commit
975b69c794
  1. 10
      pom.xml
  2. 4
      zdxt-common/zdxt-common-doc/pom.xml
  3. 22
      zdxt-common/zdxt-common-encrypt/src/main/java/org/dromara/common/encrypt/config/jasypt/CustomEncryptorConfig.java
  4. 60
      zdxt-common/zdxt-common-encrypt/src/main/java/org/dromara/common/encrypt/config/jasypt/JasyptUtils.java
  5. 17
      zdxt-modules/zdxt-enforcement-code/pom.xml
  6. 95
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/Constants.java
  7. 116
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/config/RuoYiConfig.java
  8. 522
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/CacheUtils.java
  9. 67
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/Md5Utils.java
  10. 91
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/PasswordTools.java
  11. 498
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/QRCodeUtil.java
  12. 51
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/FileTypeUtils.java
  13. 252
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/FileUploadUtils.java
  14. 138
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/FileUtils.java
  15. 55
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/MimeTypeUtils.java
  16. 24
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEnterpriseLoginController.java
  17. 42
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/common/FileController.java
  18. 9
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/EnterpriseInformation.java
  19. 11
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/enterprise/AppEnterpriseLoginBo.java
  20. 19
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/FileException.java
  21. 16
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/FileNameLengthLimitExceededException.java
  22. 16
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/FileSizeLimitExceededException.java
  23. 73
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/InvalidExtensionException.java
  24. 28
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/common/FileUploadApi.java
  25. 31
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/common/impl/FileUploadApiImpl.java
  26. 10
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/IAppEnterpriseLoginService.java
  27. 676
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java
  28. 13
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/domain/SysUser.java
  29. 10
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml
  30. 51
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml
  31. 301
      zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/AppEnterpriseLoginControllerTest.java

10
pom.xml

@ -12,13 +12,15 @@
<description>EnforcementCode管理系统</description>
<properties>
<springdoc.version>2.8.15</springdoc.version>
<knife4j.version>4.3.0</knife4j.version>
<revision>5.6.0</revision>
<spring-boot.version>3.5.12</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<mybatis.version>3.5.19</mybatis.version>
<springdoc.version>2.8.15</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version>
@ -152,6 +154,12 @@
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc</artifactId>

4
zdxt-common/zdxt-common-doc/pom.xml

@ -30,6 +30,10 @@
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.therapi</groupId>

22
zdxt-common/zdxt-common-encrypt/src/main/java/org/dromara/common/encrypt/config/jasypt/CustomEncryptorConfig.java

@ -0,0 +1,22 @@
package org.dromara.common.encrypt.config.jasypt;
import org.jasypt.encryption.StringEncryptor;
public class CustomEncryptorConfig implements StringEncryptor {
/**
* 加密解密的 密钥
*/
private String crack = "ATPfOhpnAVp6yBas7RU";
@Override
public String encrypt(String s) {
return JasyptUtils.encryptWithMD5(s,crack);
}
@Override
public String decrypt(String s) {
return JasyptUtils.decryptWithMD5(s,crack);
}
}

60
zdxt-common/zdxt-common-encrypt/src/main/java/org/dromara/common/encrypt/config/jasypt/JasyptUtils.java

@ -0,0 +1,60 @@
package org.dromara.common.encrypt.config.jasypt;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;
/**
* alibaba druid加解密规则:
* 明文密码+私钥(privateKey)加密=加密密码
* 加密密码+公钥(publicKey)解密=明文密码
*/
public final class JasyptUtils {
/**
* 加密算法
*/
private static final String PBEWITHMD5ANDDES = "PBEWithMD5AndDES";
/**
* @param text 待加密原文
* @param crack 盐值(密钥)
* @return 加密后的字符串
* @Description: Jasypt加密(PBEWithMD5AndDES)
*/
public static String encryptWithMD5(String text, String crack) {
//1.创建加解密工具实例
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
//2.加解密配置
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
config.setAlgorithm(PBEWITHMD5ANDDES);
config.setPassword(crack);
encryptor.setConfig(config);
//3.加密
return encryptor.encrypt(text);
}
/**
* @param text 待解密原文
* @param crack 盐值(密钥)
* @return 解密后的字符串
* @Description: Jasypt解密(PBEWithMD5AndDES)
*/
public static String decryptWithMD5(String text, String crack) {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
config.setAlgorithm(PBEWITHMD5ANDDES);
config.setPassword(crack);
encryptor.setConfig(config);
return encryptor.decrypt(text);
}
public static void main(String[] args) {
//加密
// String s = encryptWithMD5("jdbc:mysql://10.76.108.1:15144/zfjd?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8", "ATPfOhpnAVp6yBas7RU");
// System.out.println(s);
// String channelcs1 = decryptWithMD5("d4ohneeRrbUb1DsUxGfVs5K7/ZGrnDmSVMQOxx0GIcb0ZWzyCq9aC7CH4aBfwlq3T9G8BLy7cDfTO+IE4uPcstIfQmdHtOlqKxTbBAVxJOUArE3FykMxHzb/NGQ7k4ahHIt+QzH0HPagJACm5B3Wgz8oUZXwq9pZlHmmAG8994JaPIgMhIsA2Lxdnaw/LHBiGkq+FpE7j5TIcItow1HcGw==", "ATPfOhpnAVp6yBas7RU");
// String channelcs2 = decryptWithMD5("jGDVr5PtJpE/xa9FyHzmV2PhFrtctWjETGP5lrUlOyZ68APa81+jbQYA9RNKMI/dldAm7xxTQian8370eQqlYSAyoQV9EYlhsK3MgJkn4+OmnfBNmg227+/hizsmt1swxyW2uQbdlhzw6QaXYJ175Rjtlq1yVx+NOiamEoeSO4hzHWqxKTJT/y19fHg6QTuQc3r+PEsAFQp503HsvC5lgA==", "ATPfOhpnAVp6yBas7RU");
// System.out.println(channelcs1);
System.out.println(decryptWithMD5("zTr/tj2VgFxQyunI4eIL7NyRWDPSMPSB0VlwTYeHTdsEWyu1H96xWw==","ATPfOhpnAVp6yBas7RU"));
}
}

17
zdxt-modules/zdxt-enforcement-code/pom.xml

@ -127,6 +127,23 @@
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>

95
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/Constants.java

@ -0,0 +1,95 @@
package com.zdxt.enforcementcode.common;
public class Constants
{
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
/**
* 登录成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
public static final String LOGOUT = "Logout";
/**
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 自动去除表前缀
*/
public static final String AUTO_REOMVE_PRE = "true";
/**
* 当前记录起始索引
*/
public static final String PAGE_NUM = "pageNum";
/**
* 每页显示记录数
*/
public static final String PAGE_SIZE = "pageSize";
/**
* 排序列
*/
public static final String ORDER_BY_COLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
public static final String IS_ASC = "isAsc";
/**
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* 系统用户授权缓存
*/
public static final String SYS_AUTH_CACHE = "sys-authCache";
/**
* 参数管理 cache name
*/
public static final String SYS_CONFIG_CACHE = "sys-config";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache name
*/
public static final String SYS_DICT_CACHE = "sys-dict";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi://";
}

116
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/config/RuoYiConfig.java

@ -0,0 +1,116 @@
package com.zdxt.enforcementcode.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig
{
/** 项目名称 */
private String name;
/** 版本 */
private String version;
/** 版权年份 */
private String copyrightYear;
/** 实例演示开关 */
private boolean demoEnabled;
/** 上传路径 */
private static String profile;
/** 获取地址开关 */
private static boolean addressEnabled;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getCopyrightYear()
{
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear)
{
this.copyrightYear = copyrightYear;
}
public boolean isDemoEnabled()
{
return demoEnabled;
}
public void setDemoEnabled(boolean demoEnabled)
{
this.demoEnabled = demoEnabled;
}
public static String getProfile()
{
return profile;
}
public void setProfile(String profile)
{
RuoYiConfig.profile = profile;
}
public static boolean isAddressEnabled()
{
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled)
{
RuoYiConfig.addressEnabled = addressEnabled;
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath()
{
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath()
{
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath()
{
return getProfile() + "/upload";
}
}

522
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/CacheUtils.java

@ -0,0 +1,522 @@
package com.zdxt.enforcementcode.common.util;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 缓存工具类
*
* @author zzh
* @date 2019/8/15 19:11
*/
public class CacheUtils {
private static Logger logger = LogManager.getLogger(CacheUtils.class);
private CacheUtils() {
}
private static RedisTemplate redisTemplate;
public static void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
CacheUtils.redisTemplate = redisTemplate;
}
/**
* 获取指定 key 对应的值
*
* @param key
* @return
*/
public static <V> V get(String key) {
return (V) redisTemplate.opsForValue().get(key);
}
/**
* 获取指定 key 对应的数据类型
*
* @param key
* @return
*/
public static DataType getType(String key) {
DataType type = redisTemplate.type(key);
return type;
}
/**
* 获取匹配 pattern 对应的所有 key 对应的值,慎用会有性能问题
*
* @param pattern
* @return
*/
public static <E> Set<E> getAll(String pattern) {
final Set<String> keys = redisTemplate.keys(pattern);
Set<E> set = new HashSet<>();
for (String key : keys) {
E e = CacheUtils.get(key);
set.add(e);
}
return set;
}
/**
* 设置指定 key 对应的值到Redis,无过期时间
*
* @param key
* @param value
*/
public static void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 设置指定 key 对应的值到Redis
*
* @param key
* @param value
* @param seconds 存活时间,单位(秒)
*/
public static void set(String key, Object value, long seconds) {
redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
}
/**
* 设置指定 key 对应的值到 Redis
*
* @param key
* @param value
* @param time 存活时间,单位根据传入的时间单位
*/
public static void set(String key, Object value, long time, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, time, unit);
}
/**
* 判断指定key是否存在
*
* @param key
* @return
*/
public static boolean exists(String key) {
return redisTemplate.hasKey(key).booleanValue();
}
/**
* 根据 key 删除
*
* @param key
*/
public static boolean del(String key) {
return redisTemplate.delete(key);
}
/**
* 根据 pattern 匹配所有key,并删除
*
* @param pattern
* @return 被删除的数量
*/
public static Long delAll(String pattern) {
final Set<String> keys = redisTemplate.keys(pattern);
return redisTemplate.delete(keys);
}
/**
* 设置指定key的过期时间
*
* @param key
* @param seconds 秒
* @return
*/
public static Boolean expire(String key, long seconds) {
return redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
}
/**
* 设置指定key的过期时间
*
* @param key
* @param time
* @return
*/
public static Boolean expire(String key, long time, TimeUnit unit) {
return redisTemplate.expire(key, time, unit);
}
/**
* 设置指定key,在指定的时间过期
*
* @param key
* @param date
* @return
*/
public static Boolean expireAt(String key, Date date) {
return redisTemplate.expireAt(key, date);
}
/**
* 设置指定key,在指定的时间过期
*
* @param key
* @param instant
* @return
*/
public static Boolean expireAt(String key, Instant instant) {
return redisTemplate.expireAt(key, instant);
}
/**
* 返回指定key 的剩余存活时间,单位(秒)
*
* @param key
* @return
*/
public static long ttl(String key) {
return redisTemplate.getExpire(key).longValue();
}
/**
* 返回键 key 储存的字符串值的长度。
*
* @param key
* @return
*/
public static Long size(String key) {
return redisTemplate.opsForValue().size(key);
}
/**
* 设置指定 key 对应的hash表中的键值对
*
* @param key
* @param hashKey
* @param value
*/
public static void hset(String key, Object hashKey, Object value) {
long ttl = ttl(key);
redisTemplate.opsForHash().put(key, hashKey, value);
if(ttl > 0L) {
expire(key, ttl);
}
}
/**
* hashKey 不存在时,设置指定 key对应的hash表中的键值对,
*
* @param key
* @param hashKey
* @param value
*/
public static void hsetIfAbsent(String key, Object hashKey, Object value) {
long ttl = ttl(key);
redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
if(ttl > 0L) {
expire(key, ttl);
}
}
/**
* 将整个Map 存储为 Redis hash
*
* @param key
* @param map
*/
public static void hsetAll(String key, Map map) {
redisTemplate.opsForHash().putAll(key, map);
}
/**
* 将整个Map 存储为 Redis hash,且设置过期时间
*
* @param key
* @param map
* @param time
*/
public static void hsetAll(String key, Map map, long time, TimeUnit unit) {
redisTemplate.opsForHash().putAll(key, map);
CacheUtils.expire(key, time, unit);
}
/**
* 获取指定 key 对应的hash表中 hashKey 对应的值
*
* @param key
* @param hashKey
*/
public static <V> V hget(String key, Object hashKey) {
return (V) redisTemplate.opsForHash().get(key, hashKey);
}
/**
* 获取整个key对应的 hash
*
* @param key
* @return
*/
public static Map hgetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 获取整个key对应的 值list
*
* @param key
* @return
*/
public static List hvalues(String key) {
return redisTemplate.opsForHash().values(key);
}
/**
* 删除指定 key 对应的hash表中,hashKeys 对应的键值
*
* @param key
* @param hashKeys
*/
public static Long hdel(String key, Object... hashKeys) {
return redisTemplate.opsForHash().delete(key, hashKeys);
}
/**
* 查找 key 对应的hash表中,是否存在对应的 hashKey
* @param key
* @param hashKey
*/
public static Boolean hexists(String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
/**
* 获取指定key对应的hash长度
* @param key
* @return
*/
public static Long hsize(String key) {
return redisTemplate.opsForHash().size(key);
}
/**
* 添加 values 到指定key对应的set
*
* @param key
* @param values
*/
public static Long sadd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
/**
* 添加 values 到指定key对应的set
*
* @param key
* @param values
*/
public static Long saddAll(String key, Collection<?> values) {
if(CollectionUtils.isNotEmpty(values)) {
Object[] objects = values.toArray(new Object[values.size()]);
return redisTemplate.opsForSet().add(key, objects);
}
return 0L;
}
/**
* 删除指定key对应的set中的 values
*
* @param key
* @param values
*/
public static Long sdel(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
/**
* 获取指定key对应的set中的所有值
*
* @param key
* @return
*/
public static Set sall(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 获取指定key对应的set中是否存在给定的值
*
* @param key
* @param value
* @return
*/
public static Boolean sexists(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
/**
* 获取指定key对应的set长度
* @param key
* @return
*/
public static Long ssize(String key) {
return redisTemplate.opsForSet().size(key);
}
/**
* 自增指定 key 对应的值,从0开始
*
* @param key
* @param seconds 失效时间只针对首次自增有效
* @return
*/
public static Long incr(String key, long seconds) {
RedisAtomicLong atomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
Long increment = atomicLong.getAndIncrement();
//初始设置过期时间
if ((null == increment || increment.longValue() == 0) && seconds > 0) {
atomicLong.expire(seconds, TimeUnit.SECONDS);
}
return increment;
}
/**
* 自增指定 key 对应的值,从0开始,无过期时间
*
* @param key
* @return
*/
public static Long incr(String key) {
RedisAtomicLong atomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
Long increment = atomicLong.getAndIncrement();
return increment;
}
/**
* 增加指定的值到key对应的数据
*
* @param key
* @param delta 要减少的值
* @return
*/
public static Long incrValue(String key, long delta) {
RedisAtomicLong atomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
return atomicLong.addAndGet(delta);
}
/**
* 自减指定 key 对应的值
*
* @param key
* @return
*/
public static Long decr(String key) {
RedisAtomicLong atomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
return atomicLong.decrementAndGet();
}
/**
* 减少指定的值到key对应的数据
*
* @param key
* @param delta 要减少的值
* @return
*/
public static Long decrValue(String key, long delta) {
RedisAtomicLong atomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
return atomicLong.addAndGet(-delta);
}
/**
* key 对应的 list 增加value
*
* @param key
* @param value
* @return
*/
public static long lpush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
/**
* key 对应的 list 增加values
*
* @param key
* @param values
* @return
*/
public static long lpushAll(String key, Collection values) {
return redisTemplate.opsForList().rightPushAll(key, values);
}
/**
* 根据范围获取 指定key 对应的列表
*
* @param key
* @param start
* @param end
* @return
*/
public static List lgetRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
/**
* 根据指定 index 获取list中的值
*
* @param key
* @param index
* @return
*/
public static <V> V lget(String key, long index) {
return (V) redisTemplate.opsForList().index(key, index);
}
/**
* 根据指定 key 获取list中所有的值
*
* @param key
* @return
*/
public static <V> V lgetAll(String key) {
return (V) redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 根据指定 key 获取list 长度
*
* @param key
* @return
*/
public static Long lsize(String key) {
return redisTemplate.opsForList().size(key);
}
/**
* 根据指定 key 移除和value相等的元素,
*
* @param key
* @param count 要移除的元素数量,0:移除全部,>0:从头开始移除 count 个,<0:从尾部开始移除 count 个
* @return
*/
public static Long ldel(String key, long count, Object value) {
return redisTemplate.opsForList().remove(key, count, value);
}
}

67
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/Md5Utils.java

@ -0,0 +1,67 @@
package com.zdxt.enforcementcode.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
/**
* Md5加密方法
*
* @author ruoyi
*/
public class Md5Utils
{
private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
private static byte[] md5(String s)
{
MessageDigest algorithm;
try
{
algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(s.getBytes("UTF-8"));
byte[] messageDigest = algorithm.digest();
return messageDigest;
}
catch (Exception e)
{
log.error("MD5 Error...", e);
}
return null;
}
private static final String toHex(byte hash[])
{
if (hash == null)
{
return null;
}
StringBuffer buf = new StringBuffer(hash.length * 2);
int i;
for (i = 0; i < hash.length; i++)
{
if ((hash[i] & 0xff) < 0x10)
{
buf.append("0");
}
buf.append(Long.toString(hash[i] & 0xff, 16));
}
return buf.toString();
}
public static String hash(String s)
{
try
{
return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8");
}
catch (Exception e)
{
log.error("not supported charset...{}", e);
return s;
}
}
}

91
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/PasswordTools.java

@ -0,0 +1,91 @@
package com.zdxt.enforcementcode.common.util;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
/**
* 密码工具类
* 加盐加密 / 加盐解密
*/
public class PasswordTools {
/**
*加盐加密
* @param password
* @return 加密后的密码
*/
public static String encrypt(String password){
//生成盐值,使用随机字符串UUID,UUID为32位的
String salt = UUID.randomUUID().toString().replace("-","");
//(盐值+密码)进行MD5加密,DigestUtils.md5DigestAsHex参数为字节数组,生成一个十六进制32位的字符串
String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
//将盐值和加密的密码组合返回,最终存储到数据库中的密码
String dbPassword = salt + "$" + finalPassword;
return dbPassword;
}
/**
* 校验密码
* @param password
* @param dbPassword
* @return
*/
public static boolean decrypt(String password,String dbPassword){
if(StringUtils.hasLength(password) && StringUtils.hasLength(dbPassword)
&& dbPassword.length()==65 && dbPassword.contains("$")){
String[] arr = dbPassword.split("\\$");
String salt = arr[0];
//根据获取的盐值生成校验的密码
String checkPassword = salt + "$" + DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
//用校验密码与数据库保存的密码进行校验
if(checkPassword.equals(dbPassword)){
return true;
}
}
return false;
}
/**
* 字符串256 加密
* @param str
* @return
*/
public static String SHA256(String str){
try {
// 创建SHA-256哈希算法的MessageDigest对象
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
// 使用SHA-256计算输入字符串的哈希值
byte[] hash = sha256.digest(str.getBytes("UTF-8"));
// 将字节转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String dbPassword = encrypt("88879ayqwt");
System.out.println(decrypt("88879ayqwt",dbPassword)); //true
System.out.println(decrypt("45678",dbPassword)); //false
}
}

498
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/QRCodeUtil.java

@ -0,0 +1,498 @@
package com.zdxt.enforcementcode.common.util;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
public class QRCodeUtil {
private static final String CHARSET = "utf-8";
private static final String FORMAT_NAME = "JPG";
// 二维码尺寸
private static final int QRCODE_SIZE = 300;
// LOGO宽度
private static final int WIDTH = 60;
// LOGO高度
private static final int HEIGHT = 60;
private static BufferedImage createImage(String content, String imgPath,
boolean needCompress) throws Exception {
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000
: 0xFFFFFFFF);
}
}
if (imgPath == null || "".equals(imgPath)) {
return image;
}
// 插入图片
QRCodeUtil.insertImage(image, imgPath, needCompress);
return image;
}
/**
* 插入LOGO
*
* @param source
* 二维码图片
* @param imgPath
* LOGO图片地址
* @param needCompress
* 是否压缩
* @throws Exception
*/
private static void insertImage(BufferedImage source, String imgPath,
boolean needCompress) throws Exception {
File file = new File(imgPath);
if (!file.exists()) {
System.err.println(""+imgPath+" 该文件不存在!");
return;
}
Image src = ImageIO.read(new File(imgPath));
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress) { // 压缩LOGO
if (width > WIDTH) {
width = WIDTH;
}
if (height > HEIGHT) {
height = HEIGHT;
}
Image image = src.getScaledInstance(width, height,
Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 绘制缩小后的图
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content
* 内容
* @param imgPath
* LOGO地址
* @param destPath
* 存放目录
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static File encodeFile(String content, String imgPath, String destPath,
boolean needCompress) throws Exception {
String file = new Random().nextInt(99999999)+".jpg";
File file1 = new File(destPath + "/" + file);
BufferedImage image = QRCodeUtil.createImage(content, imgPath,
needCompress);
mkdirs(destPath);
ImageIO.write(image, FORMAT_NAME, file1);
return file1;
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content
* 内容
* @param imgPath
* LOGO地址
* @param destPath
* 存放目录
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static String encode(String content, String imgPath, String destPath,
boolean needCompress) throws Exception {
String file = new Random().nextInt(99999999)+".jpg";
BufferedImage image = QRCodeUtil.createImage(content, imgPath,
needCompress);
mkdirs(destPath);
ImageIO.write(image, FORMAT_NAME, new File(destPath + "/" + file));
return destPath + "/" + file;
}
/**
* 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
* @param destPath 存放目录
*/
public static void mkdirs(String destPath) {
File file =new File(destPath);
//当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
if (!file.exists() && !file.isDirectory()) {
file.mkdirs();
}
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content
* 内容
* @param imgPath
* LOGO地址
* @param destPath
* 存储地址
* @throws Exception
*/
public static void encode(String content, String imgPath, String destPath)
throws Exception {
QRCodeUtil.encode(content, imgPath, destPath, false);
}
/**
* 生成二维码
*
* @param content
* 内容
* @param destPath
* 存储地址
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static void encode(String content, String destPath,
boolean needCompress) throws Exception {
QRCodeUtil.encode(content, null, destPath, needCompress);
}
/**
* 生成二维码
*
* @param content
* 内容
* @param destPath
* 存储地址
* @throws Exception
*/
public static void encode(String content, String destPath) throws Exception {
QRCodeUtil.encode(content, null, destPath, false);
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content
* 内容
* @param imgPath
* LOGO地址
* @param output
* 输出流
* @param needCompress
* 是否压缩LOGO
* @throws Exception
*/
public static void encode(String content, String imgPath,
OutputStream output, boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath,
needCompress);
ImageIO.write(image, FORMAT_NAME, output);
}
/**
* 生成二维码
*
* @param content
* 内容
* @param output
* 输出流
* @throws Exception
*/
public static void encode(String content, OutputStream output)
throws Exception {
QRCodeUtil.encode(content, null, output, false);
}
/**
* 解析二维码
*
* @param file
* 二维码图片
* @return
* @throws Exception
*/
public static String decode(File file) throws Exception {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(
image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
result = new MultiFormatReader().decode(bitmap, hints);
String resultStr = result.getText();
return resultStr;
}
/**
* 解析二维码
*
* @param path
* 二维码图片地址
* @return
* @throws Exception
*/
public static String decode(String path) throws Exception {
return QRCodeUtil.decode(new File(path));
}
/**
*
* @param content
* @param imgPath
* @param destPath
* @param localUrl 当前环境的域名或者ip地址
* @param needCompress
* @return
* @throws Exception
*/
public static String encode(String content, String imgPath, String destPath,String localUrl,
boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath,
needCompress);
mkdirs(destPath);
String file = new Random().nextInt(99999999)+".jpg";
ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));
return localUrl+file;
}
public static String encodeDZ(String content, String imgPath, String destPath,String localUrl,int length,int width,
boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImageDZ(content, imgPath,length,width,
needCompress);
mkdirs(destPath);
String file = new Random().nextInt(99999999)+".jpg";
ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));
return localUrl+file;
}
private static BufferedImage createImageDZ(String content, String imgPath,int newlength,int newWidth,
boolean needCompress) throws Exception {
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, newlength, newWidth, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000
: 0xFFFFFFFF);
}
}
if (imgPath == null || "".equals(imgPath)) {
return image;
}
// 插入图片
QRCodeUtil.insertImage(image, imgPath, needCompress);
return image;
}
/**
* 将图片url转换成base64流
* @param url
* @return
*/
public static String getBase64(String url){
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
Map hints = new HashMap();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //设置字符集编码类型
hints.put(EncodeHintType.MARGIN, 1); //设置白边
BitMatrix bitMatrix = null;
try {
bitMatrix = multiFormatWriter.encode(url, BarcodeFormat.QR_CODE, 300, 300,hints);
BufferedImage image = toBufferedImage(bitMatrix);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//输出二维码图片流
try {
ImageIO.write(image, "jpg",outputStream);
// String base = Base64Utils.encodeToString(outputStream.toByteArray());
byte[] bytes = outputStream.toByteArray();
String base = Base64.getEncoder().encodeToString(bytes); // ✅ 使用 Java 标准库
return base;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (WriterException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return null;
}
public static BufferedImage toBufferedImage(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);//0xFF000000黑;0xFFFFFFFF白
}
}
return image;
}
// 蓝色二维码前景色
private static final int BLUE_FOREGROUND_COLOR = 0xFF003399;
// classpath中的LOGO图片路径
private static final String EMBEDDED_LOGO_PATH = "images/enterprise_pub_qrcode_logo.jpg";
/**
* 生成蓝色二维码并在中间插入自定义图片,返回BufferedImage
*
* @param content 二维码内容
* @param imgPath 中间图片地址(为空则不插入图片)
* @param needCompress 是否压缩中间图片
* @return BufferedImage
* @throws Exception
*/
public static BufferedImage createBlueQrCode(String content, String imgPath,
boolean needCompress) throws Exception {
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? BLUE_FOREGROUND_COLOR
: 0xFFFFFFFF);
}
}
if (imgPath != null && !"".equals(imgPath)) {
insertImage(image, imgPath, needCompress);
}
return image;
}
/**
* 生成蓝色二维码并在中间插入classpath中的LOGO图片,返回BufferedImage
*
* @param content 二维码内容
* @return BufferedImage
* @throws Exception
*/
public static BufferedImage createBlueQrCodeWithLogo(String content,int size) throws Exception {
Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, size, size, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? BLUE_FOREGROUND_COLOR
: 0xFFFFFFFF);
}
}
// 从classpath读取LOGO图片并插入二维码中间
InputStream logoStream = QRCodeUtil.class.getClassLoader().getResourceAsStream(EMBEDDED_LOGO_PATH);
if (logoStream != null) {
Image logo = ImageIO.read(logoStream);
logoStream.close();
int logoWidth = Math.min(logo.getWidth(null), 100);
int logoHeight = Math.min(logo.getHeight(null), 100);
Image scaledLogo = logo.getScaledInstance(logoWidth, logoHeight, Image.SCALE_SMOOTH);
Graphics2D graph = image.createGraphics();
int x = (size - logoWidth) / 2;
int y = (size - logoHeight) / 2;
graph.drawImage(scaledLogo, x, y, logoWidth, logoHeight, null);
Shape shape = new RoundRectangle2D.Float(x, y, logoWidth, logoHeight, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
return image;
}
public static MultipartFile getMultipartFile(File file) {
try (InputStream input = new FileInputStream(file)) {
return new MockMultipartFile(
"file", // 字段名
file.getName(), // 原始文件名
MediaType.IMAGE_JPEG_VALUE, // 文件类型
input.readAllBytes() // 文件内容
);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid file: " + e, e);
}
}
public static void main(String[] args) throws Exception {
String text = "https://www.baidu.com/s?ie=UTF-8&wd=%E7%99%BE%E5%BA%A6";
//这里设置自定义网站url
String logoPath =null;
String destPath = "d:\\pic\\";
// String url=QRCodeUtil.encode(text, logoPath, destPath, true);
// System.out.println(url);
/* String file="D:\\11.jpg";
String info=QRCodeUtil.decode(file);
System.out.println(info);*/
// 将地址转换为二维码图片 base64编码返回给页面
String base64 = getBase64(text);
System.out.println("data:image/jpeg;base64," + base64);
}
}

51
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/FileTypeUtils.java

@ -0,0 +1,51 @@
package com.zdxt.enforcementcode.common.util.file;
import org.dromara.common.core.utils.StringUtils;
import java.io.File;
/**
* Created with IntelliJ IDEA.
* Description:
* User: ouyunhui
* Date: 2020-12-08
* Time: 15:59
*/
public class FileTypeUtils {
/**
* 获取文件类型
* <p>
* 例如: ruoyi.txt, 返回: txt
*
* @param file 文件名
* @return 后缀(不含".")
*/
public static String getFileType(File file)
{
if (null == file)
{
return StringUtils.EMPTY;
}
return getFileType(file.getName());
}
/**
* 获取文件类型
* <p>
* 例如: ruoyi.txt, 返回: txt
*
* @param fileName 文件名
* @return 后缀(不含".")
*/
public static String getFileType(String fileName)
{
int separatorIndex = fileName.lastIndexOf(".");
if (separatorIndex < 0)
{
return "";
}
return fileName.substring(separatorIndex + 1).toLowerCase();
}
}

252
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/FileUploadUtils.java

@ -0,0 +1,252 @@
package com.zdxt.enforcementcode.common.util.file;
import com.zdxt.enforcementcode.common.Constants;
import com.zdxt.enforcementcode.common.config.RuoYiConfig;
import com.zdxt.enforcementcode.common.util.Md5Utils;
import com.zdxt.enforcementcode.exception.FileSizeLimitExceededException;
import com.zdxt.enforcementcode.exception.InvalidExtensionException;
import org.apache.commons.io.FilenameUtils;
import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* 文件上传工具类
*
* @author ruoyi
*/
public class FileUploadUtils
{
/**
* 默认大小 50M
*/
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
/**
* 默认的文件名最大长度 100
*/
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
/**
* 常见的视频类型
*/
public static final String VIDEO_TYPE = "rm|rmvb|wmv|avi|mp4|3gp|mkvrm|rmvb|avi|mp4|3gp|mkv";
/**
* 默认上传的地址
*/
private static String defaultBaseDir = RuoYiConfig.getProfile();
private static int counter = 0;
public static void setDefaultBaseDir(String defaultBaseDir)
{
FileUploadUtils.defaultBaseDir = defaultBaseDir;
}
public static String getDefaultBaseDir()
{
return defaultBaseDir;
}
/**
* 以默认配置进行文件上传
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
public static final String upload(MultipartFile file) throws IOException
{
try
{
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
}
catch (Exception e)
{
throw new IOException(e.getMessage(), e);
}
}
/**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException`
*/
public static final String upload(String baseDir, MultipartFile file) throws IOException
{
try
{
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
}
catch (Exception e)
{
throw new IOException(e.getMessage(), e);
}
}
/**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws Exception
{
int fileNamelength = file.getOriginalFilename().length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new Exception("文件名称过长");
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
File desc = getAbsoluteFile(baseDir, fileName);
file.transferTo(desc);
String pathFileName = getPathFileName(baseDir, fileName);
return pathFileName;
}
/**
* 编码文件名
*/
public static final String extractFilename(MultipartFile file)
{
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
fileName = DateUtils.datePath() + "/" + encodingFilename(fileName) + "." + extension;
return fileName;
}
private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
{
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
if (!desc.exists())
{
desc.createNewFile();
}
return desc;
}
private static final String getPathFileName(String uploadDir, String fileName) throws IOException
{
int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
return pathFileName;
}
/**
* 编码文件名
*/
private static final String encodingFilename(String fileName)
{
fileName = fileName.replace("_", " ");
fileName = Md5Utils.hash(fileName + System.nanoTime() + counter++);
return fileName;
}
/**
* 文件大小校验
*
* @param file 上传的文件
* @return
*/
public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
throws Exception
{
long size = file.getSize();
int begin = file.getOriginalFilename().lastIndexOf(".");
int last = file.getOriginalFilename().length();
//获得文件后缀名
String a = file.getOriginalFilename().substring(begin+1, last);
if(VIDEO_TYPE.indexOf(a) == -1){ //判断是否是视频
if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
{
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
}
}
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
{
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
{
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
fileName);
}
else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
{
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
fileName);
}
else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
{
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
fileName);
}
else
{
throw new InvalidExtensionException(allowedExtension, extension, fileName);
}
}
}
/**
* 判断MIME类型是否是允许的MIME类型
*
* @param extension
* @param allowedExtension
* @return
*/
public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
{
for (String str : allowedExtension)
{
if (str.equalsIgnoreCase(extension))
{
return true;
}
}
return false;
}
/**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public static final String getExtension(MultipartFile file)
{
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (StringUtils.isEmpty(extension))
{
extension = MimeTypeUtils.getExtension(file.getContentType());
}
return extension;
}
}

138
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/FileUtils.java

@ -0,0 +1,138 @@
package com.zdxt.enforcementcode.common.util.file;
import jakarta.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URLEncoder;
/**
* 文件处理工具类
*
* @author ruoyi
*/
public class FileUtils
{
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
/**
* 输出指定文件的byte数组
*
* @param filePath 文件路径
* @param os 输出流
* @return
*/
public static void writeBytes(String filePath, OutputStream os) throws IOException
{
FileInputStream fis = null;
try
{
File file = new File(filePath);
if (!file.exists())
{
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0)
{
os.write(b, 0, length);
}
}
catch (IOException e)
{
throw e;
}
finally
{
if (os != null)
{
try
{
os.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
if (fis != null)
{
try
{
fis.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
}
/**
* 删除文件
*
* @param filePath 文件
* @return
*/
public static boolean deleteFile(String filePath)
{
boolean flag = false;
File file = new File(filePath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists())
{
file.delete();
flag = true;
}
return flag;
}
/**
* 文件名称验证
*
* @param filename 文件名称
* @return true 正常 false 非法
*/
public static boolean isValidFilename(String filename)
{
return filename.matches(FILENAME_PATTERN);
}
/**
* 下载文件名重新编码
*
* @param request 请求对象
* @param fileName 文件名
* @return 编码后的文件名
*/
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
{
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains("MSIE"))
{
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
}
else if (agent.contains("Firefox"))
{
// 火狐浏览器
filename = new String(fileName.getBytes(), "ISO8859-1");
}
else if (agent.contains("Chrome"))
{
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
else
{
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}

55
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/file/MimeTypeUtils.java

@ -0,0 +1,55 @@
package com.zdxt.enforcementcode.common.util.file;
/**
* 媒体类型工具类
*
* @author ruoyi
*/
public class MimeTypeUtils
{
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
public static final String[] FLASH_EXTENSION = { "swf", "flv" };
public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb" };
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// pdf
"pdf","apk", "mp4" , "mkv"};
public static String getExtension(String prefix)
{
switch (prefix)
{
case IMAGE_PNG:
return "png";
case IMAGE_JPG:
return "jpg";
case IMAGE_JPEG:
return "jpeg";
case IMAGE_BMP:
return "bmp";
case IMAGE_GIF:
return "gif";
default:
return "";
}
}
}

24
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEnterpriseLoginController.java

@ -1,18 +1,22 @@
package com.zdxt.enforcementcode.controller.app.enterprise;
import cn.dev33.satoken.annotation.SaIgnore;
import com.zdxt.enforcementcode.domain.bo.enterprise.AppEnterpriseLoginBo;
import com.zdxt.enforcementcode.service.enterprise.IAppEnterpriseLoginService;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* 企业端-用户登录
*/
@ -20,24 +24,20 @@ import org.springframework.web.bind.annotation.RestController;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app-ent/login")
@RequestMapping("/enterprise/login")
public class AppEnterpriseLoginController {
@Autowired
private IAppEnterpriseLoginService appEnterpriseLoginService;
/**
* 企业端用户登录
* 企业端用户登录(深i企)
*/
@SaIgnore
@ApiResponse(description = "企业端-用户登录")
@PostMapping("siqLogin")
@ResponseBody
public Boolean siqLogin(AppEnterpriseLoginBo bo){
Boolean flag = appEnterpriseLoginService.addSiqUser(bo);
return flag;
@PostMapping("/siqLogin")
public R<Map<String, Object>> siqLogin(@RequestBody AppEnterpriseLoginBo bo){
Map<String, Object> result = appEnterpriseLoginService.siqLogin(bo);
return R.ok("登录成功", result);
}
}

42
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/common/FileController.java

@ -0,0 +1,42 @@
package com.zdxt.enforcementcode.controller.common;
import com.zdxt.enforcementcode.service.common.FileUploadApi;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件上传 控制层
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/common/file")
public class FileController extends BaseController {
private final FileUploadApi fileUploadApi;
/**
* 上传文件
*
* @param file 文件
*/
@Log(title = "文件上传", businessType = BusinessType.INSERT)
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<String> upload(@RequestPart("file") MultipartFile file) {
String filePath = fileUploadApi.upload(file);
return R.ok("上传成功", filePath);
}
}

9
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/EnterpriseInformation.java

@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
@ -19,7 +20,7 @@ import java.io.Serial;
*/
@Data
@TableName("enterprise_information")
public class EnterpriseInformation implements Serializable {
public class EnterpriseInformation extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
@ -34,13 +35,13 @@ public class EnterpriseInformation implements Serializable {
* 覆盖父类字段: 数据库中create_by存储的是人名字符串而非用户ID,跳过查询避免类型转换异常
*/
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
private Long createBy;
/**
* 覆盖父类字段: 数据库中update_by存储的是人名字符串而非用户ID,跳过查询避免类型转换异常
*/
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
private Long updateBy;
/**
* 企业名称
@ -95,7 +96,7 @@ public class EnterpriseInformation implements Serializable {
/**
* 部门ID
*/
private Long deptId;
private String deptId;
/**
* 二维码id

11
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/enterprise/AppEnterpriseLoginBo.java

@ -8,7 +8,18 @@ import lombok.Data;
@JsonIgnoreProperties(ignoreUnknown = true)
public class AppEnterpriseLoginBo {
/**
* 用户ID
*/
private String userId;
/**
* 用户名称
*/
private String userName;
/**
* 用户码
*/
private String code;
}

19
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/FileException.java

@ -0,0 +1,19 @@
package com.zdxt.enforcementcode.exception;
import org.dromara.common.core.exception.base.BaseException;
/**
* 文件信息异常类
*
* @author ruoyi
*/
public class FileException extends BaseException
{
private static final long serialVersionUID = 1L;
public FileException(String code, Object[] args)
{
super("file", code, args, null);
}
}

16
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/FileNameLengthLimitExceededException.java

@ -0,0 +1,16 @@
package com.zdxt.enforcementcode.exception;
/**
* 文件名称超长限制异常类
*
* @author ruoyi
*/
public class FileNameLengthLimitExceededException extends FileException
{
private static final long serialVersionUID = 1L;
public FileNameLengthLimitExceededException(int defaultFileNameLength)
{
super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
}
}

16
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/FileSizeLimitExceededException.java

@ -0,0 +1,16 @@
package com.zdxt.enforcementcode.exception;
/**
* 文件名大小限制异常类
*
* @author ruoyi
*/
public class FileSizeLimitExceededException extends FileException
{
private static final long serialVersionUID = 1L;
public FileSizeLimitExceededException(long defaultMaxSize)
{
super("upload.exceed.maxSize", new Object[] { defaultMaxSize });
}
}

73
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/exception/InvalidExtensionException.java

@ -0,0 +1,73 @@
package com.zdxt.enforcementcode.exception;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import java.util.Arrays;
/**
* 文件上传 误异常类
*
* @author ruoyi
*/
public class InvalidExtensionException extends FileUploadException
{
private static final long serialVersionUID = 1L;
private String[] allowedExtension;
private String extension;
private String filename;
public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
{
super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
this.allowedExtension = allowedExtension;
this.extension = extension;
this.filename = filename;
}
public String[] getAllowedExtension()
{
return allowedExtension;
}
public String getExtension()
{
return extension;
}
public String getFilename()
{
return filename;
}
public static class InvalidImageExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidFlashExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidMediaExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
}

28
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/common/FileUploadApi.java

@ -0,0 +1,28 @@
package com.zdxt.enforcementcode.service.common;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 文件上传API
*
* @author QingWang.Li
* @date 2020/3/20 16:44
*/
@RestController
public interface FileUploadApi {
/**
* 上传文件
*
* @param file 上传的文件
* @return 文件路径
* @throws IOException
*/
String upload(MultipartFile file);
}

31
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/common/impl/FileUploadApiImpl.java

@ -0,0 +1,31 @@
package com.zdxt.enforcementcode.service.common.impl;
import com.zdxt.enforcementcode.common.util.file.FileUploadUtils;
import com.zdxt.enforcementcode.service.common.FileUploadApi;
import org.dromara.common.core.exception.base.BaseException;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 文件上传API-实现类
*
* @author QingWang.Li
* @date 2020/3/20 16:50
*/
@Service
public class FileUploadApiImpl implements FileUploadApi {
@Override
public String upload(MultipartFile file) {
String filePath = FileUploadUtils.getDefaultBaseDir();
try {
return FileUploadUtils.upload(filePath, file);
} catch (IOException e) {
throw new BaseException("上传失败");
}
}
}

10
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/IAppEnterpriseLoginService.java

@ -4,12 +4,18 @@ import com.alibaba.fastjson.JSONObject;
import com.zdxt.enforcementcode.domain.bo.enterprise.AppEnterpriseLoginBo;
import org.dromara.system.domain.vo.SysUserVo;
import java.util.Map;
/**
* 企业端-企业用户登录Service接口
*/
public interface IAppEnterpriseLoginService {
Boolean addSiqUser(AppEnterpriseLoginBo bo);
/**
* 深I企登录
* @param bo 登录参数
* @return 登录结果,包含token和用户信息
*/
Map<String, Object> siqLogin(AppEnterpriseLoginBo bo);
}

676
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java

@ -1,40 +1,57 @@
package com.zdxt.enforcementcode.service.enterprise.impl;
import cn.hutool.core.util.ArrayUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zdxt.enforcementcode.common.util.ApiConstants;
import com.zdxt.enforcementcode.common.util.HttpUtil;
import com.zdxt.enforcementcode.common.util.IdCardCryptoUtil;
import com.zdxt.enforcementcode.common.util.UserConstants;
import com.zdxt.enforcementcode.common.util.*;
import com.zdxt.enforcementcode.domain.EnterpriseInformation;
import com.zdxt.enforcementcode.domain.EnterpriseUser;
import com.zdxt.enforcementcode.domain.EnterpriseUserRef;
import com.zdxt.enforcementcode.domain.bo.QrCodeGenerateHistoryBo;
import com.zdxt.enforcementcode.domain.bo.enterprise.AppEnterpriseLoginBo;
import com.zdxt.enforcementcode.mapper.EnterpriseInformationMapper;
import com.zdxt.enforcementcode.mapper.EnterpriseUserMapper;
import com.zdxt.enforcementcode.mapper.EnterpriseUserRefMapper;
import com.zdxt.enforcementcode.service.IQrCodeGenerateHistoryService;
import com.zdxt.enforcementcode.service.common.FileUploadApi;
import com.zdxt.enforcementcode.service.enterprise.IAppEnterpriseLoginService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.SysUserPost;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.mapper.SysUserPostMapper;
import org.dromara.system.mapper.SysUserRoleMapper;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 企业端-登录Service业务层处理
* <p>
* 实现深i企第三方登录的完整业务流程,使用 Redis 分布式锁防止并发注册
* </p>
*
* @author Lion Li
* @date 2026-05-09
*/
@Slf4j
@RequiredArgsConstructor
@ -43,14 +60,15 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
@Value("${siq.code}")
private String code;
@Value("${siq.secretKey}")
private String secretKey;
@Value("${siq.siqUrl}")
private String siqUrl;
@Autowired
private SysUserMapper sysUserMapper;
private RedissonClient redissonClient;
@Autowired
private SysUserPostMapper sysUserPostMapper;
@ -61,261 +79,461 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
@Autowired
private EnterpriseInformationMapper enterpriseInformationMapper;
@Autowired
private EnterpriseUserMapper enterpriseUserMapper;
@Autowired
private EnterpriseUserRefMapper enterpriseUserRefMapper;
@Autowired
private IQrCodeGenerateHistoryService iQrCodeGenerateHistoryService;
@Autowired
private FileUploadApi fileUploadApi;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@Value("${sjdm.profile}")
private String filePath;
@Value("${sjdm.qr_code_url}")
private String qrCodeUrl;
@Value("${siq.mock.enabled:false}")
private boolean mockEnabled;
/**
* 深i企用户登录并自动注册(带分布式锁防重)
*
* @param bo 登录参数
* @return 登录结果,包含token和用户信息
*/
@Override
public Boolean addSiqUser(AppEnterpriseLoginBo bo) {
//调用深i企登录接口
JSONObject userInfo =siqLoginInfo(bo);
//判断用户信息
Map<String, Object> checkUserInfo = checkUserInfo(userInfo);
if (UserConstants.USER_NAME_NOT_UNIQUE.equals(checkUserInfo.get("unique"))) {
return false;
public Map<String, Object> siqLogin(AppEnterpriseLoginBo bo) {
JSONObject userInfo;
if (mockEnabled) {
log.info("使用模拟数据进行深i企登录测试");
userInfo = getMockSiqUserInfo(bo.getCode());
} else {
userInfo = siqLoginInfo(bo);
}
if (checkUserInfo.get("userId") != null) {
return false;
String siqUserId = userInfo.getString("userId");
String lockKey = "siq_login_lock:" + siqUserId;
try {
log.info("深i企登录获取分布式锁成功: lockKey={}, userId={}", lockKey, siqUserId);
EnterpriseUser newUser = saveOrUpdateEnterpriseUser(userInfo);
EnterpriseInformation enterpriseInformation = addSiqUserCompany(userInfo, newUser);
if (newUser != null && enterpriseInformation != null) {
createUserEnterpriseRef(newUser.getUserId(), enterpriseInformation);
}
String token = generateToken(newUser, enterpriseInformation);
Map<String, Object> result = new HashMap<>();
result.put("token", token);
result.put("userId", newUser.getUserId());
result.put("userName", newUser.getUserName());
result.put("loginName", newUser.getLoginName());
result.put("phonenumber", newUser.getPhonenumber() != null ? newUser.getPhonenumber() : "");
result.put("enterpriseId", enterpriseInformation.getId());
result.put("enterpriseName", enterpriseInformation.getEnterpriseName());
result.put("enterpriseNumber", enterpriseInformation.getEnterpriseNumber());
return result;
} catch (Exception e) {
log.error("深i企登录处理异常: lockKey={}", lockKey, e);
throw new BaseException("登录处理失败:" + e.getMessage());
}
//封装添加用户信息
String phone =checkUserInfo.get("phone").toString();
SysUser sysUser = addUserInfo(userInfo, phone );
//用户密码加密
Boolean result=false;
result =encryptPassword(sysUser);
//添加深i企用户关联企业信息
if (result == true) {
result= addSiqUserCompany(userInfo);
}
/**
* 保存或更新企业用户信息
* <p>
* 根据 userName 和 userType 判断用户是否存在:
* - 不存在:插入新用户
* - 存在:更新用户信息
* </p>
*
* @param userInfo 深i企返回的用户信息
* @return 是否操作成功
*/
private EnterpriseUser saveOrUpdateEnterpriseUser(JSONObject userInfo) {
String loginName = userInfo.getString("account");
String userType = "siq";
EnterpriseUser newUser = null;
if (StringUtils.isEmpty(loginName)) {
log.error("深i企用户账号为空");
throw new BaseException("深i企用户账号不能为空");
}
// 用户登录-需要生成Token (暂时搁置)
return result;
LambdaQueryWrapper<EnterpriseUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(EnterpriseUser::getLoginName, loginName)
.eq(EnterpriseUser::getUserType, userType);
EnterpriseUser existUser = enterpriseUserMapper.selectOne(queryWrapper);
Long userId;
if (existUser == null) {
log.info("企业用户不存在,执行新增操作: userName={}, userType={}", loginName, userType);
newUser = buildEnterpriseUser(userInfo, userType);
enterpriseUserMapper.insert(newUser);
} else {
log.info("企业用户已存在,执行更新操作: userId={}, loginName={}", existUser.getUserId(), loginName);
newUser = buildEnterpriseUser(userInfo, userType);
newUser.setUserId(existUser.getUserId());
enterpriseUserMapper.updateById(newUser);
}
return newUser;
}
/**
* 调用深i企登录接口返回用户登录信息
* 构建企业用户对象
*
* @param userInfo 深i企返回的用户信息
* @param userType 用户类型
* @return 企业用户对象
*/
private EnterpriseUser buildEnterpriseUser(JSONObject userInfo, String userType) {
EnterpriseUser user = new EnterpriseUser();
String phone = IdCardCryptoUtil.symmetricDecryptIdCard(secretKey, userInfo.getString("phone"));
// 基本信息
user.setUserName(userInfo.getString("userName"));
user.setUserType(userType);
user.setLoginName(userInfo.getString("account"));
user.setPhonenumber(phone);
user.setNickName(userInfo.getString("userName"));
public JSONObject siqLoginInfo(AppEnterpriseLoginBo bo){
// 设置默认部门ID
user.setDeptId(UserConstants.DEFAULT_DEPT);
Map<String, Object> params = new HashMap<>();
// 设置默认密码(MD5加盐加密)
String defaultPassword = UserConstants.DEFAULT_PASSWORD;
user.setPassword(DigestUtil.md5Hex(user.getLoginName() + defaultPassword + user.getSalt()));
String accessKeySign = IdCardCryptoUtil.symmetricEncryptIdCard(secretKey,bo.getCode());
params.put("accessKeySign",accessKeySign);
params.put("accessKeySign{}",accessKeySign);
params.put("appCode",code);
// 账号状态:正常
user.setStatus("0");
String data = HttpUtil.httpSendPost(siqUrl + ApiConstants.API_SIQUSERLOGIN, JSONObject.toJSONString(params),new HashMap<>());
JSONObject jsonObject = JSONObject.parseObject(data);
JSONObject userInfo = jsonObject.getJSONObject("data");
// 删除标志:存在
user.setDelFlag("0");
return userInfo;
// 最后登录时间
user.setLoginDate(DateUtils.getNowDate());
// 备注
user.setRemark("深i企同步用户");
return user;
}
/**
* 判断用户登录信息
* 调用深i企登录接口返回用户登录信息
*
* @param bo 登录参数,包含授权码
* @return 用户信息JSON对象
*/
public Map<String, Object> checkUserInfo(JSONObject userInfo) {
Map<String, Object> map = new HashMap<>();
// 判断用户唯一校验
String userUnique;
LambdaQueryWrapper<SysUser> queryAccountWrapper = new LambdaQueryWrapper<>();
queryAccountWrapper.eq(SysUser::getLoginName, userInfo.getString("account"))
.eq(SysUser::getUserSource, UserConstants.DEFAULT_USER_SOURCE);
userUnique = String.valueOf(sysUserMapper.selectCount(queryAccountWrapper));
if (UserConstants.USER_NAME_NOT_UNIQUE.equals(userUnique)) {
map.put("unique", UserConstants.USER_NAME_NOT_UNIQUE);
return map;
}
LambdaQueryWrapper<SysUser> queryPhoneWrapper = new LambdaQueryWrapper<>();
queryPhoneWrapper.eq(SysUser::getLoginName, userInfo.getString("phone"))
.eq(SysUser::getUserSource, UserConstants.DEFAULT_USER_SOURCE);
userUnique = String.valueOf(sysUserMapper.selectCount(queryPhoneWrapper));
public JSONObject siqLoginInfo(AppEnterpriseLoginBo bo) {
try {
Map<String, Object> params = new HashMap<>();
String accessKeySign = IdCardCryptoUtil.symmetricEncryptIdCard(secretKey, bo.getCode());
params.put("accessKeySign", accessKeySign);
params.put("appCode", code);
String data = HttpUtil.httpSendPost(
siqUrl + ApiConstants.API_SIQUSERLOGIN,
JSONObject.toJSONString(params),
new HashMap<>()
);
JSONObject jsonObject = JSONObject.parseObject(data);
JSONObject userInfo = jsonObject.getJSONObject("data");
if (userInfo == null) {
log.error("深i企接口返回数据为空: {}", data);
throw new BaseException("深i企接口返回数据异常");
}
if (UserConstants.USER_NAME_NOT_UNIQUE.equals(userUnique)) {
map.put("unique", UserConstants.USER_NAME_NOT_UNIQUE);
return map;
}
log.debug("深i企登录返回用户信息: userId={}", userInfo.getString("userId"));
return userInfo;
// 判断用户名是否重复
String phone = IdCardCryptoUtil.symmetricDecryptIdCard(secretKey, userInfo.getString("phone"));
LambdaQueryWrapper<SysUser> queryLoginNameWrapper = new LambdaQueryWrapper<>();
queryLoginNameWrapper.eq(SysUser::getLoginName, phone)
.eq(SysUser::getUserSource, UserConstants.DEFAULT_USER_SOURCE);
SysUser sysUser = sysUserMapper.selectOne(queryLoginNameWrapper);
if (sysUser != null && sysUser.getUserId() != null) {
map.put("userId", sysUser.getUserId());
map.put("phone", phone);
return map;
} catch (BaseException e) {
throw e;
} catch (Exception e) {
log.error("调用深i企接口异常", e);
throw new BaseException("获取深i企用户信息失败");
}
return map;
}
/**
* 封装添加用户信息
* 添加深i企用户关联企业信息
*
* @param userInfo 深i企返回的企业信息
* @return 是否添加成功
*/
public SysUser addUserInfo(JSONObject userInfo,String phone) {
//新增用户信息
SysUser user = new SysUser();
if (StringUtils.isNotEmpty(userInfo.getString("account"))) {
// 设置默认用户权限,权限默认值
user.setRoleId(UserConstants.DEFAULT_ROLE);
user.setUserName(userInfo.getString("userName"));
if (StringUtils.isNotEmpty(userInfo.getString("account"))) {
user.setLoginName(userInfo.getString("account"));
} else {
user.setLoginName(phone);
public EnterpriseInformation addSiqUserCompany(JSONObject userInfo, EnterpriseUser newUser) {
String enterpriseNumber = userInfo.getString("corpCid");
LambdaQueryWrapper<EnterpriseInformation> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(EnterpriseInformation::getEnterpriseNumber, enterpriseNumber);
EnterpriseInformation existingEnterprise = enterpriseInformationMapper.selectOne(queryWrapper);
if (existingEnterprise != null) {
log.info("企业已存在,执行更新操作: enterpriseNumber={}, enterpriseId={}",
enterpriseNumber, existingEnterprise.getId());
existingEnterprise.setEnterpriseName(userInfo.getString("corpName"));
existingEnterprise.setCompanyOrgType(userInfo.getString("companyOrgType"));
String establishDate = userInfo.getString("establishDate");
try {
if (StrUtil.isNotEmpty(establishDate)) {
Date establishDateD = format.parse(establishDate);
existingEnterprise.setEstablishmentTime(establishDateD);
}
} catch (Exception e) {
log.warn("解析企业成立日期失败: {}", establishDate, e);
}
user.setPhonenumber(phone);
// 设置默认密码
user.setPassword(UserConstants.DEFAULT_PASSWORD);
user.setCreateTime(DateUtils.getNowDate());
// 设置默认部门ID
user.setDeptId(UserConstants.DEFAULT_DEPT);
// 设置用户类型
user.setUserSource(UserConstants.DEFAULT_USER_SOURCE);
user.setDelFlag(UserConstants.NORMAL_DEL_FLAG);
}else if(StringUtils.isNotEmpty(userInfo.getString("phone"))){
// 设置默认用户权限,权限默认值
user.setRoleId(UserConstants.DEFAULT_ROLE);
user.setUserName(userInfo.getString("userName"));
if (StringUtils.isNotEmpty(userInfo.getString("account"))) {
user.setLoginName(userInfo.getString("account"));
} else {
user.setLoginName(phone);
existingEnterprise.setBusinessScope(userInfo.getString("businessScope"));
existingEnterprise.setEnterpriseAddress(userInfo.getString("regLocation"));
existingEnterprise.setRegisteredCapital(userInfo.getString("regCapital"));
existingEnterprise.setEnterpriseTel(
IdCardCryptoUtil.symmetricDecryptIdCard(secretKey, userInfo.getString("phone"))
);
existingEnterprise.setLegalPerson(userInfo.getString("legalPersonName"));
existingEnterprise.setUserId(newUser.getUserId());
String termStart = userInfo.getString("termStart");
try {
if (StrUtil.isNotEmpty(termStart)) {
Date termStartD = format.parse(termStart);
termStart = format.format(termStartD);
}
} catch (Exception e) {
termStart = "";
log.warn("解析营业期限开始时间失败: {}", termStart, e);
}
String termEnd = userInfo.getString("termEnd");
try {
if (StrUtil.isNotEmpty(termEnd)) {
Date termEndD = format.parse(termEnd);
termEnd = format.format(termEndD);
}
} catch (Exception e) {
termEnd = "";
log.warn("解析营业期限结束时间失败: {}", termEnd, e);
}
existingEnterprise.setOperatingTerm(termStart);
existingEnterprise.setOperatingTermEnd(termEnd);
existingEnterprise.setUpdateTime(DateUtils.getNowDate());
existingEnterprise.setUpdateBy(newUser.getUserId());
enterpriseInformationMapper.updateById(existingEnterprise);
log.info("企业信息更新成功: enterpriseNumber={}, enterpriseId={}",
enterpriseNumber, existingEnterprise.getId());
return existingEnterprise;
} else {
log.info("企业不存在,执行新增操作: enterpriseNumber={}", enterpriseNumber);
EnterpriseInformation enterpriseInformation = new EnterpriseInformation();
enterpriseInformation.setId(IdUtil.simpleUUID());
enterpriseInformation.setCreateTime(DateUtils.getNowDate());
enterpriseInformation.setCreateBy(newUser.getUserId());
enterpriseInformation.setDeptId(newUser.getDeptId());
String salt = PasswordTools.encrypt(enterpriseNumber);
enterpriseInformation.setSalt(salt);
enterpriseInformation.setEnterpriseNumber(enterpriseNumber);
enterpriseInformation.setEnterpriseName(userInfo.getString("corpName"));
enterpriseInformation.setCompanyOrgType(userInfo.getString("companyOrgType"));
String establishDate = userInfo.getString("establishDate");
try {
if (StrUtil.isNotEmpty(establishDate)) {
Date establishDateD = format.parse(establishDate);
enterpriseInformation.setEstablishmentTime(establishDateD);
}
} catch (Exception e) {
log.warn("解析企业成立日期失败: {}", establishDate, e);
}
enterpriseInformation.setBusinessScope(userInfo.getString("businessScope"));
enterpriseInformation.setEnterpriseAddress(userInfo.getString("regLocation"));
enterpriseInformation.setRegisteredCapital(userInfo.getString("regCapital"));
enterpriseInformation.setEnterpriseTel(
IdCardCryptoUtil.symmetricDecryptIdCard(secretKey, userInfo.getString("phone"))
);
enterpriseInformation.setLegalPerson(userInfo.getString("legalPersonName"));
enterpriseInformation.setUserId(newUser.getUserId());
String termStart = userInfo.getString("termStart");
try {
if (StrUtil.isNotEmpty(termStart)) {
Date termStartD = format.parse(termStart);
termStart = format.format(termStartD);
}
} catch (Exception e) {
termStart = "";
log.warn("解析营业期限开始时间失败: {}", termStart, e);
}
String termEnd = userInfo.getString("termEnd");
try {
if (StrUtil.isNotEmpty(termEnd)) {
Date termEndD = format.parse(termEnd);
termEnd = format.format(termEndD);
}
} catch (Exception e) {
termEnd = "";
log.warn("解析营业期限结束时间失败: {}", termEnd, e);
}
user.setPhonenumber(phone);
// 设置默认密码
user.setPassword(UserConstants.DEFAULT_PASSWORD);
user.setCreateTime(DateUtils.getNowDate());
// 设置默认部门ID
user.setDeptId(UserConstants.DEFAULT_DEPT);
// 设置用户类型
user.setUserSource(UserConstants.DEFAULT_USER_SOURCE);
user.setDelFlag(UserConstants.NORMAL_DEL_FLAG);
enterpriseInformation.setOperatingTerm(termStart);
enterpriseInformation.setOperatingTermEnd(termEnd);
enterpriseInformationMapper.insert(enterpriseInformation);
log.info("企业信息保存成功: enterpriseNumber={}, enterpriseId={}",
enterpriseInformation.getEnterpriseNumber(), enterpriseInformation.getId());
/**生成二维码**/
qrCodeGenerate(enterpriseInformation);
return enterpriseInformation;
}
return user ;
}
/**
* 用户密码加密
*
* @return
*/
public Boolean encryptPassword(SysUser user) {
// 用户密码加密
user.setPassword(DigestUtil.md5Hex(user.getLoginName()+user.getPassword()+user.getSalt()));
// 新增用户
int rows = 0;
Boolean flag=false;
rows= sysUserMapper.insert(user);
// 新增岗位
if (ArrayUtil.isNotEmpty(user.getPostIds())) {
List<SysUserPost> userPostList = new ArrayList<>();
for (Long postId : user.getPostIds()) {
SysUserPost userPost = new SysUserPost();
userPost.setUserId(user.getUserId());
userPost.setPostId(postId);
userPostList.add(userPost);
}
flag= sysUserPostMapper.insertBatch(userPostList);
}
// 新增用户与角色管理
if (ArrayUtil.isNotEmpty(user.getRoleId())) {
List<SysUserRole> userRoleList = new ArrayList<>();
for (Long roleId : user.getRoleIds()) {
SysUserRole userPost = new SysUserRole();
userPost.setUserId(user.getUserId());
userPost.setRoleId(roleId);
userRoleList.add(userPost);
}
flag=sysUserRoleMapper.insertBatch(userRoleList);
private void qrCodeGenerate(EnterpriseInformation enterpriseInformation){
String uuid = IdUtil.simpleUUID();
Map<String, String> jsonMap = new HashMap<>();
jsonMap.put("salt", enterpriseInformation.getSalt());
jsonMap.put("codeId", uuid);
jsonMap.put("type", "zfjd");
String text = JSON.toJSONString(jsonMap);
//这里设置自定义网站url
String logoPath = qrCodeUrl;
try {
File encode = QRCodeUtil.encodeFile(text, logoPath, filePath, true);
MultipartFile multipartFile = QRCodeUtil.getMultipartFile(encode);
String upload = fileUploadApi.upload(multipartFile);
// String uuid = IdUtil.simpleUUID();
QrCodeGenerateHistoryBo qrCodeGenerateHistory = new QrCodeGenerateHistoryBo();
qrCodeGenerateHistory.setId(uuid);
//生成时间
qrCodeGenerateHistory.setGenerateTime(DateUtils.getNowDate());
//二维码地址
qrCodeGenerateHistory.setQrCodeUrl(upload);
//统一信用代码
qrCodeGenerateHistory.setEnterpriseNumber(enterpriseInformation.getEnterpriseNumber());
//变更原因
qrCodeGenerateHistory.setReasonsChange("登记");
iQrCodeGenerateHistoryService.insertByBo(qrCodeGenerateHistory);
} catch (Exception e) {
e.printStackTrace();
}
}
if (rows==1 && flag==true) {
return true;
}else{
return false;
private void createUserEnterpriseRef(Long userId, EnterpriseInformation enterpriseInformation) {
LambdaQueryWrapper<EnterpriseUserRef> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(EnterpriseUserRef::getUserId, userId)
.eq(EnterpriseUserRef::getEnterpriseId, enterpriseInformation.getId());
Long count = enterpriseUserRefMapper.selectCount(queryWrapper);
if (count > 0) {
log.info("用户与企业关联关系已存在: userId={}, enterpriseId={}", userId, enterpriseInformation.getId());
return;
}
}
EnterpriseUser enterpriseUser = enterpriseUserMapper.selectById(userId);
EnterpriseUserRef userRef = new EnterpriseUserRef();
userRef.setRefId(UUID.randomUUID().toString().replace("-", ""));
userRef.setUserId(userId);
userRef.setUserName(enterpriseUser != null ? enterpriseUser.getUserName() : "");
userRef.setEnterpriseId(enterpriseInformation.getId());
userRef.setEnterpriseName(enterpriseInformation.getEnterpriseName());
userRef.setEnterpriseNumber(enterpriseInformation.getEnterpriseNumber());
userRef.setRemark("深i企登录自动关联");
enterpriseUserRefMapper.insert(userRef);
log.info("用户与企业关联关系创建成功: userId={}, userName={}, enterpriseId={}, enterpriseName={}",
userId, userRef.getUserName(), enterpriseInformation.getId(), enterpriseInformation.getEnterpriseName());
}
// 添加深i企用户关联企业信息
/**
* 添加深i企用户关联企业信息
* 生成token
* @param user
* @param enterpriseInformation
* @return
*/
public Boolean addSiqUserCompany(JSONObject userInfo){
EnterpriseInformation enterpriseInformation = new EnterpriseInformation();
// 企业纳税识别号
enterpriseInformation.setEnterpriseNumber(userInfo.getString("corpCid"));
LambdaQueryWrapper<EnterpriseInformation> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(EnterpriseInformation::getEnterpriseNumber, enterpriseInformation.getEnterpriseNumber());
if (enterpriseInformationMapper.selectCount(queryWrapper) > 0) {
return false;
}
//企业名称
enterpriseInformation.setEnterpriseName(userInfo.getString("corpName"));
//企业类型
enterpriseInformation.setCompanyOrgType(userInfo.getString("companyOrgType"));
//注册时间
String establishDate = userInfo.getString("establishDate");
try {
if (StrUtil.isNotEmpty(establishDate)) {
Date establishDateD = format.parse(establishDate);
enterpriseInformation.setEstablishmentTime(establishDateD);
}
} catch (Exception e) {
log.warn("解析企业成立日期失败: {}", establishDate, e);
}
//经营范围
enterpriseInformation.setBusinessScope(userInfo.getString("businessScope"));
//注册地址
enterpriseInformation.setEnterpriseAddress(userInfo.getString("regLocation"));
//注册资金
enterpriseInformation.setRegisteredCapital(userInfo.getString("regCapital"));
//企业联系人电话
enterpriseInformation.setEnterpriseTel(IdCardCryptoUtil.symmetricDecryptIdCard(secretKey,userInfo.getString("phone")));
//企业法人
enterpriseInformation.setLegalPerson(userInfo.getString("legalPersonName"));
//用户Id
enterpriseInformation.setUserId(Long.valueOf(userInfo.getString("userId")));
//营业时间
//开始时间
String termStart = userInfo.getString("termStart");
try {
Date termStartD = format.parse(termStart);
termStart = format.format(termStartD);
} catch (Exception e) {
termStart = "";
}
//结束时间 返回为空表示为永久
String termEnd = userInfo.getString("termEnd");
try {
if(StrUtil.isNotEmpty(termEnd)){
Date termEndD = format.parse(termEnd);
termEnd = format.format(termEndD);
}
} catch (Exception e) {
termEnd = "";
}
//营业期限-开始时间
enterpriseInformation.setOperatingTerm(termStart);
//营业期限-结束时间 为空表示 无固定期限
enterpriseInformation.setOperatingTermEnd(termEnd);
return enterpriseInformationMapper.insert(enterpriseInformation)>0? Boolean.TRUE:Boolean.FALSE;
}
// 深i企用户登录
public SysUserVo siqLogin(JSONObject userInfo){
SysUserVo sysUserVo = new SysUserVo();
return sysUserVo;
private String generateToken(EnterpriseUser user, EnterpriseInformation enterpriseInformation) {
LoginUser loginUser = new LoginUser();
loginUser.setUserId(user.getUserId());
loginUser.setUsername(user.getLoginName());
loginUser.setNickname(user.getUserName());
loginUser.setUserType(user.getUserType());
loginUser.setDeptId(String.valueOf(user.getDeptId()));
loginUser.setTenantId("000000");
loginUser.setDeptId(enterpriseInformation.getId());
loginUser.setDeptName(enterpriseInformation.getEnterpriseName());
SaLoginParameter model = new SaLoginParameter();
model.setDeviceType("app");
model.setExtra(LoginHelper.CLIENT_KEY, "enterprise_app");
model.setTimeout(86400);
model.setActiveTimeout(1800);
LoginHelper.login(loginUser, model);
String token = StpUtil.getTokenValue();
log.info("生成token成功: userId={}, token={}", user.getUserId(), token);
return token;
}
/**
* 获取模拟的深i企用户信息(用于测试)
*
* @param code 授权码
* @return 模拟的用户信息JSON对象
*/
private JSONObject getMockSiqUserInfo(String code) {
JSONObject userInfo = new JSONObject();
userInfo.put("userId", "100001");
userInfo.put("account", "test_user_" + (code != null ? code.hashCode() % 1000 : 1));
userInfo.put("userName", "测试用户");
userInfo.put("phone", IdCardCryptoUtil.symmetricEncryptIdCard(secretKey, "13800138000"));
userInfo.put("corpCid", "91440300788302281W");
userInfo.put("corpName", "吉田拉链(深圳)有限公司公明工厂");
userInfo.put("companyOrgType", "有限责任公司");
userInfo.put("establishDate", "2010-01-15");
userInfo.put("businessScope", "生产经营拉链、服装辅料及相关产品");
userInfo.put("regLocation", "深圳市光明新区公明街道塘尾社区宝塘工业区");
userInfo.put("regCapital", "5000万元人民币");
userInfo.put("legalPersonName", "张三");
userInfo.put("termStart", "2010-01-15");
userInfo.put("termEnd", "2040-01-14");
log.info("返回模拟深i企用户信息: userId={}, account={}, corpName={}",
userInfo.getString("userId"),
userInfo.getString("account"),
userInfo.getString("corpName"));
return userInfo;
}
}

13
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/domain/SysUser.java

@ -265,21 +265,8 @@ public class SysUser extends TenantEntity {
@TableField("rylb")
private String rylb;
private Long roleId;
/**
* 岗位组
* @param userId
*/
private Long[] postIds;
/**
* 角色组
* @param userId
*/
private Long[] roleIds;
public SysUser(Long userId) {
this.userId = userId;
}

10
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml

@ -107,13 +107,13 @@ spring:
spring.data:
redis:
# 地址
host: localhost
host: 139.186.148.21
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# redis 密码必须配置
#password: ruoyi123
password: zdxt@123
# 连接超时时间
timeout: 10s
# 是否开启ssl
@ -294,4 +294,8 @@ siq:
siqUrl: http://egbpub.siqhz.com/egb/api/thirdauth
code: zfjdm
# secretKey: eWIzY3BMRVNEQXhvUUlXMA==
secretKey: ENC(zTr/tj2VgFxQyunI4eIL7NyRWDPSMPSB0VlwTYeHTdsEWyu1H96xWw==)
# secretKey: ENC(zTr/tj2VgFxQyunI4eIL7NyRWDPSMPSB0VlwTYeHTdsEWyu1H96xWw==)
secretKey: SXRpWEhVN3RHM3ZFbzRQcQ==
# 模拟数据开关(true=使用模拟数据,false=调用真实接口)
mock:
enabled: true

51
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml

@ -223,45 +223,17 @@ api-decrypt:
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
components:
securitySchemes:
Authorization:
type: apiKey
in: header
name: Authorization
description: "Bearer Token,格式: Bearer {token}"
clientid:
type: apiKey
in: header
name: clientid
description: "客户端ID"
info:
# 标题
title: '标题:zdxt-Vue-Plus多租户管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: ${project.version}'
# 作者信息
contact:
name: Lion Li
email: crazylionli@163.com
url: https://gitee.com/dromara/zdxt-Vue-Plus
#这里定义了两个分组,可定义多个,也可以不定义
group-configs:
- group: enforcementcode
packages-to-scan: com.zdxt.enforcementcode
# - group: 2.通用模块
# packages-to-scan: org.dromara.web
# - group: 3.系统模块
# packages-to-scan: org.dromara.system
# - group: 4.代码生成模块
# packages-to-scan: org.dromara.generator
# - group: 5.工作流模块
# packages-to-scan: org.dromara.workflow
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui
default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
knife4j:
enable: true
setting:
language: zh_cn
# 防止XSS攻击
xss:
# 过滤开关
@ -321,3 +293,8 @@ warm-flow:
node-tooltip: true
# 默认Authorization,如果有多个token,用逗号分隔
token-name: ${sa-token.token-name},clientid
# 项目相关配置
sjdm:
profile: /usr/local/uploadPath
qr_code_url: /usr/local/uploads/zfjd_logo.png

301
zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/AppEnterpriseLoginControllerTest.java

@ -0,0 +1,301 @@
//package com.zdxt.rest.test;
//
//import cn.dev33.satoken.context.mock.SaTokenContextMockUtil;
//import org.dromara.ZdxtAdminApplication;
//import org.junit.jupiter.api.*;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
//import org.springframework.boot.test.context.SpringBootTest;
//import org.springframework.http.MediaType;
//import org.springframework.test.web.servlet.MockMvc;
//import org.springframework.test.web.servlet.MvcResult;
//
//import java.nio.charset.StandardCharsets;
//
//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
//
///**
// * AppEnterpriseLoginController 企业端-用户登录接口测试
// * <p>
// * 覆盖以下接口:
// * 1. POST /app-ent/login/siqLogin - 深i企用户登录
// * </p>
// * <p>
// * 注意:此测试需要配置真实的深i企接口地址,或在测试环境中 Mock 相关服务
// * </p>
// *
// * @author zdxt
// */
//@SpringBootTest(classes = ZdxtAdminApplication.class)
//@AutoConfigureMockMvc
//@DisplayName("AppEnterpriseLoginController 企业端-用户登录接口测试")
//@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
//public class AppEnterpriseLoginControllerTest {
//
// @Autowired
// private MockMvc mockMvc;
//
// private static final String BASE_URL = "/app-ent/login";
// private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e";
//
// @BeforeEach
// public void setUp() {
// SaTokenContextMockUtil.setMockContext();
// }
//
// @AfterEach
// public void tearDown() {
// SaTokenContextMockUtil.clearContext();
// }
//
// // ==================== siqLogin 接口测试 ====================
//
// @DisplayName("深i企登录 - 正常请求(需要有效的授权码)")
// @Test
// @Order(1)
// public void testSiqLoginSuccess() throws Exception {
// String requestBody = "{\"code\":\"test_authorization_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
// System.out.println("深i企登录接口响应: " + responseBody);
//
// if (responseBody.contains("\"code\":200")) {
// Assertions.assertTrue(responseBody.contains("token"), "返回数据应包含 token");
// Assertions.assertTrue(responseBody.contains("userId"), "返回数据应包含 userId");
// Assertions.assertTrue(responseBody.contains("userName"), "返回数据应包含 userName");
// Assertions.assertTrue(responseBody.contains("enterpriseId"), "返回数据应包含 enterpriseId");
// Assertions.assertTrue(responseBody.contains("enterpriseName"), "返回数据应包含 enterpriseName");
// System.out.println("深i企登录成功,返回了完整的用户和企业信息");
// } else {
// System.out.println("深i企登录失败(可能由于授权码无效或深i企接口未配置): " + responseBody);
// }
// }
//
// @DisplayName("深i企登录 - 缺少授权码参数应返回失败")
// @Test
// @Order(2)
// public void testSiqLoginMissingCode() throws Exception {
// String requestBody = "{}";
//
// mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andExpect(jsonPath("$.code").value(500));
//
// System.out.println("缺少授权码参数返回失败测试成功");
// }
//
// @DisplayName("深i企登录 - 空授权码应返回失败")
// @Test
// @Order(3)
// public void testSiqLoginEmptyCode() throws Exception {
// String requestBody = "{\"code\":\"\"}";
//
// mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andExpect(jsonPath("$.code").value(500));
//
// System.out.println("空授权码返回失败测试成功");
// }
//
// @DisplayName("深i企登录 - 无效授权码应返回失败")
// @Test
// @Order(4)
// public void testSiqLoginInvalidCode() throws Exception {
// String requestBody = "{\"code\":\"invalid_code_12345\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
//
// if (responseBody.contains("\"code\":500")) {
// System.out.println("无效授权码返回失败测试成功");
// } else {
// System.out.println("注意:无效授权码未返回预期错误,响应: " + responseBody);
// }
// }
//
// @DisplayName("深i企登录 - 验证返回数据结构完整性")
// @Test
// @Order(5)
// public void testSiqLoginResponseStructure() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
// System.out.println("验证返回数据结构响应: " + responseBody);
//
// if (responseBody.contains("\"code\":200")) {
// Assertions.assertTrue(responseBody.contains("\"data\""), "返回数据应包含 data 字段");
//
// String data = extractDataJson(responseBody);
// if (data != null) {
// Assertions.assertTrue(data.contains("token"), "data 中应包含 token");
// Assertions.assertTrue(data.contains("userId"), "data 中应包含 userId");
// Assertions.assertTrue(data.contains("userName"), "data 中应包含 userName");
// Assertions.assertTrue(data.contains("loginName"), "data 中应包含 loginName");
// Assertions.assertTrue(data.contains("phonenumber"), "data 中应包含 phonenumber");
// Assertions.assertTrue(data.contains("enterpriseId"), "data 中应包含 enterpriseId");
// Assertions.assertTrue(data.contains("enterpriseName"), "data 中应包含 enterpriseName");
// Assertions.assertTrue(data.contains("enterpriseNumber"), "data 中应包含 enterpriseNumber");
// System.out.println("返回数据结构完整,包含所有必需字段");
// }
// } else {
// System.out.println("登录失败,无法验证数据结构");
// }
// }
//
// @DisplayName("深i企登录 - 重复登录应能正常处理(同一授权码多次调用)")
// @Test
// @Order(6)
// public void testSiqLoginDuplicateCall() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// for (int i = 1; i <= 3; i++) {
// System.out.println("========== 第 " + i + " 次调用深i企登录 ==========");
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
// System.out.println("第 " + i + " 次调用响应: " + responseBody);
//
// Thread.sleep(1000);
// }
//
// System.out.println("========== 重复登录测试完成 ==========");
// }
//
// @DisplayName("深i企登录 - 验证 Token 格式")
// @Test
// @Order(7)
// public void testSiqLoginTokenFormat() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
//
// if (responseBody.contains("\"code\":200")) {
// String token = extractTokenFromResponse(responseBody);
// if (token != null && !token.isEmpty()) {
// Assertions.assertTrue(token.length() > 10, "Token 长度应大于10个字符");
// System.out.println("Token 格式验证通过,Token: " + token);
// } else {
// System.out.println("未能提取到 Token");
// }
// } else {
// System.out.println("登录失败,无法验证 Token 格式");
// }
// }
//
// @DisplayName("深i企登录 - 验证企业信息关联")
// @Test
// @Order(8)
// public void testSiqLoginEnterpriseAssociation() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
//
// if (responseBody.contains("\"code\":200")) {
// String data = extractDataJson(responseBody);
// if (data != null) {
// String enterpriseId = extractJsonValue(data, "enterpriseId");
// String enterpriseName = extractJsonValue(data, "enterpriseName");
// String enterpriseNumber = extractJsonValue(data, "enterpriseNumber");
//
// if (enterpriseId != null && enterpriseName != null && enterpriseNumber != null) {
// Assertions.assertFalse(enterpriseId.isEmpty(), "企业ID不应为空");
// Assertions.assertFalse(enterpriseName.isEmpty(), "企业名称不应为空");
// Assertions.assertFalse(enterpriseNumber.isEmpty(), "企业统一信用代码不应为空");
// System.out.println("企业信息关联验证通过");
// System.out.println("企业ID: " + enterpriseId);
// System.out.println("企业名称: " + enterpriseName);
// System.out.println("企业统一信用代码: " + enterpriseNumber);
// }
// }
// } else {
// System.out.println("登录失败,无法验证企业信息关联");
// }
// }
//
// // ==================== 工具方法 ====================
//
// /**
// * 从 JSON 响应中提取 data 字段
// */
// private String extractDataJson(String responseBody) {
// if (responseBody != null && responseBody.contains("\"data\":{")) {
// int start = responseBody.indexOf("\"data\":{") + 7;
// int end = responseBody.lastIndexOf("}");
// if (start > 0 && end > start) {
// return responseBody.substring(start, end + 1);
// }
// }
// return null;
// }
//
// /**
// * 从响应中提取 Token
// */
// private String extractTokenFromResponse(String responseBody) {
// return extractJsonValue(responseBody, "token");
// }
//
// /**
// * 从 JSON 字符串中提取指定字段的值
// */
// private String extractJsonValue(String json, String key) {
// if (json != null && json.contains("\"" + key + "\"")) {
// int start = json.indexOf("\"" + key + "\":\"") + key.length() + 4;
// int end = json.indexOf("\"", start);
// if (start > 0 && end > start) {
// return json.substring(start, end);
// }
// }
// return null;
// }
//}
//{
//}
Loading…
Cancel
Save