Commit 0ae9af04927fa3a161789e58037915537f8d2f5e
1 parent
3cc7a35c
增加 MyBatis Plus 的 EncryptTypeHandler 类型处理器,实现字段的加密解密
Showing
8 changed files
with
99 additions
and
24 deletions
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
| @@ -58,6 +58,14 @@ | @@ -58,6 +58,14 @@ | ||
| 58 | <groupId>com.baomidou</groupId> | 58 | <groupId>com.baomidou</groupId> |
| 59 | <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> | 59 | <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 --> |
| 60 | </dependency> | 60 | </dependency> |
| 61 | + | ||
| 62 | + <!-- 工具类相关 --> | ||
| 63 | + <dependency> | ||
| 64 | + <groupId>com.github.ulisesbocchio</groupId> | ||
| 65 | + <artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 --> | ||
| 66 | + <optional>true</optional> | ||
| 67 | + </dependency> | ||
| 68 | + | ||
| 61 | </dependencies> | 69 | </dependencies> |
| 62 | 70 | ||
| 63 | </project> | 71 | </project> |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.mybatis.core.type; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 5 | +import org.apache.ibatis.type.BaseTypeHandler; | ||
| 6 | +import org.apache.ibatis.type.JdbcType; | ||
| 7 | +import org.jasypt.encryption.StringEncryptor; | ||
| 8 | + | ||
| 9 | +import java.sql.CallableStatement; | ||
| 10 | +import java.sql.PreparedStatement; | ||
| 11 | +import java.sql.ResultSet; | ||
| 12 | +import java.sql.SQLException; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 字段字段的 TypeHandler 实现类,基于 {@link StringEncryptor} 实现 | ||
| 16 | + * 可通过 jasypt.encryptor.password 配置项,设置密钥 | ||
| 17 | + * | ||
| 18 | + * @author 芋道源码 | ||
| 19 | + */ | ||
| 20 | +public class EncryptTypeHandler extends BaseTypeHandler<String> { | ||
| 21 | + | ||
| 22 | + private static StringEncryptor encryptor; | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { | ||
| 26 | + ps.setString(i, getEncryptor().encrypt(parameter)); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public String getNullableResult(ResultSet rs, String columnName) throws SQLException { | ||
| 31 | + String value = rs.getString(columnName); | ||
| 32 | + return getResult(value); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { | ||
| 37 | + String value = rs.getString(columnIndex); | ||
| 38 | + return getResult(value); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Override | ||
| 42 | + public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { | ||
| 43 | + String value = cs.getString(columnIndex); | ||
| 44 | + return getResult(value); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + private String getResult(String value) { | ||
| 48 | + if (value == null) { | ||
| 49 | + return null; | ||
| 50 | + } | ||
| 51 | + return getEncryptor().decrypt(value); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + private StringEncryptor getEncryptor() { | ||
| 55 | + if (encryptor != null) { | ||
| 56 | + return encryptor; | ||
| 57 | + } | ||
| 58 | + encryptor = SpringUtil.getBean(StringEncryptor.class); | ||
| 59 | + Assert.notNull(encryptor, "StringEncryptor 不能为空"); | ||
| 60 | + return encryptor; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | +} |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/StringLiSTTypeHandler.java renamed to yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/StringListTypeHandler.java
| @@ -21,7 +21,7 @@ import java.util.List; | @@ -21,7 +21,7 @@ import java.util.List; | ||
| 21 | */ | 21 | */ |
| 22 | @MappedJdbcTypes(JdbcType.VARCHAR) | 22 | @MappedJdbcTypes(JdbcType.VARCHAR) |
| 23 | @MappedTypes(List.class) | 23 | @MappedTypes(List.class) |
| 24 | -public class StringLiSTTypeHandler implements TypeHandler<List<String>> { | 24 | +public class StringListTypeHandler implements TypeHandler<List<String>> { |
| 25 | 25 | ||
| 26 | private static final String COMMA = ","; | 26 | private static final String COMMA = ","; |
| 27 | 27 |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java
| 1 | package cn.iocoder.yudao.module.infra.dal.dataobject.db; | 1 | package cn.iocoder.yudao.module.infra.dal.dataobject.db; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.type.EncryptTypeHandler; | ||
| 4 | import com.baomidou.mybatisplus.annotation.KeySequence; | 5 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 6 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 5 | import com.baomidou.mybatisplus.annotation.TableName; | 7 | import com.baomidou.mybatisplus.annotation.TableName; |
| 6 | import lombok.Data; | 8 | import lombok.Data; |
| 7 | 9 | ||
| @@ -10,7 +12,7 @@ import lombok.Data; | @@ -10,7 +12,7 @@ import lombok.Data; | ||
| 10 | * | 12 | * |
| 11 | * @author 芋道源码 | 13 | * @author 芋道源码 |
| 12 | */ | 14 | */ |
| 13 | -@TableName("infra_data_source_config") | 15 | +@TableName(value = "infra_data_source_config", autoResultMap = true) |
| 14 | @KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 16 | @KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 15 | @Data | 17 | @Data |
| 16 | public class DataSourceConfigDO extends BaseDO { | 18 | public class DataSourceConfigDO extends BaseDO { |
| @@ -40,6 +42,7 @@ public class DataSourceConfigDO extends BaseDO { | @@ -40,6 +42,7 @@ public class DataSourceConfigDO extends BaseDO { | ||
| 40 | /** | 42 | /** |
| 41 | * 密码 | 43 | * 密码 |
| 42 | */ | 44 | */ |
| 45 | + @TableField(typeHandler = EncryptTypeHandler.class) | ||
| 43 | private String password; | 46 | private String password; |
| 44 | 47 | ||
| 45 | } | 48 | } |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java
| @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; | @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; | ||
| 8 | import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; | 8 | import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; |
| 9 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; | 9 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; |
| 10 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; | 10 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; |
| 11 | -import org.jasypt.encryption.StringEncryptor; | ||
| 12 | import org.springframework.stereotype.Service; | 11 | import org.springframework.stereotype.Service; |
| 13 | import org.springframework.validation.annotation.Validated; | 12 | import org.springframework.validation.annotation.Validated; |
| 14 | 13 | ||
| @@ -33,9 +32,6 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | @@ -33,9 +32,6 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | ||
| 33 | private DataSourceConfigMapper dataSourceConfigMapper; | 32 | private DataSourceConfigMapper dataSourceConfigMapper; |
| 34 | 33 | ||
| 35 | @Resource | 34 | @Resource |
| 36 | - private StringEncryptor stringEncryptor; | ||
| 37 | - | ||
| 38 | - @Resource | ||
| 39 | private DynamicDataSourceProperties dynamicDataSourceProperties; | 35 | private DynamicDataSourceProperties dynamicDataSourceProperties; |
| 40 | 36 | ||
| 41 | @Override | 37 | @Override |
| @@ -44,7 +40,6 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | @@ -44,7 +40,6 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | ||
| 44 | checkConnectionOK(dataSourceConfig); | 40 | checkConnectionOK(dataSourceConfig); |
| 45 | 41 | ||
| 46 | // 插入 | 42 | // 插入 |
| 47 | - dataSourceConfig.setPassword(stringEncryptor.encrypt(createReqVO.getPassword())); | ||
| 48 | dataSourceConfigMapper.insert(dataSourceConfig); | 43 | dataSourceConfigMapper.insert(dataSourceConfig); |
| 49 | // 返回 | 44 | // 返回 |
| 50 | return dataSourceConfig.getId(); | 45 | return dataSourceConfig.getId(); |
| @@ -58,7 +53,6 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | @@ -58,7 +53,6 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | ||
| 58 | checkConnectionOK(updateObj); | 53 | checkConnectionOK(updateObj); |
| 59 | 54 | ||
| 60 | // 更新 | 55 | // 更新 |
| 61 | - updateObj.setPassword(stringEncryptor.encrypt(updateObj.getPassword())); | ||
| 62 | dataSourceConfigMapper.updateById(updateObj); | 56 | dataSourceConfigMapper.updateById(updateObj); |
| 63 | } | 57 | } |
| 64 | 58 | ||
| @@ -83,12 +77,7 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | @@ -83,12 +77,7 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService { | ||
| 83 | return buildMasterDataSourceConfig(); | 77 | return buildMasterDataSourceConfig(); |
| 84 | } | 78 | } |
| 85 | // 从 DB 中读取 | 79 | // 从 DB 中读取 |
| 86 | - DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(id); | ||
| 87 | - try { | ||
| 88 | - dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword())); | ||
| 89 | - } catch (Exception ignore) { // 解码失败,则不解码 | ||
| 90 | - } | ||
| 91 | - return dataSourceConfig; | 80 | + return dataSourceConfigMapper.selectById(id); |
| 92 | } | 81 | } |
| 93 | 82 | ||
| 94 | @Override | 83 | @Override |
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java
| 1 | package cn.iocoder.yudao.module.infra.service.db; | 1 | package cn.iocoder.yudao.module.infra.service.db; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.ReflectUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.type.EncryptTypeHandler; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils; | 5 | import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils; |
| 4 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 6 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 5 | import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO; | 7 | import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO; |
| @@ -8,8 +10,10 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; | @@ -8,8 +10,10 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; | ||
| 8 | import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; | 10 | import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; |
| 9 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; | 11 | import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; |
| 10 | import org.jasypt.encryption.StringEncryptor; | 12 | import org.jasypt.encryption.StringEncryptor; |
| 13 | +import org.junit.jupiter.api.BeforeEach; | ||
| 11 | import org.junit.jupiter.api.Test; | 14 | import org.junit.jupiter.api.Test; |
| 12 | import org.mockito.MockedStatic; | 15 | import org.mockito.MockedStatic; |
| 16 | +import org.mockito.stubbing.Answer; | ||
| 13 | import org.springframework.boot.test.mock.mockito.MockBean; | 17 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 14 | import org.springframework.context.annotation.Import; | 18 | import org.springframework.context.annotation.Import; |
| 15 | 19 | ||
| @@ -21,7 +25,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId | @@ -21,7 +25,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId | ||
| 21 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 25 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 22 | import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS; | 26 | import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS; |
| 23 | import static org.junit.jupiter.api.Assertions.*; | 27 | import static org.junit.jupiter.api.Assertions.*; |
| 24 | -import static org.mockito.Mockito.*; | 28 | +import static org.mockito.ArgumentMatchers.anyString; |
| 29 | +import static org.mockito.ArgumentMatchers.eq; | ||
| 30 | +import static org.mockito.Mockito.mockStatic; | ||
| 31 | +import static org.mockito.Mockito.when; | ||
| 25 | 32 | ||
| 26 | /** | 33 | /** |
| 27 | * {@link DataSourceConfigServiceImpl} 的单元测试类 | 34 | * {@link DataSourceConfigServiceImpl} 的单元测试类 |
| @@ -43,13 +50,20 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | @@ -43,13 +50,20 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | ||
| 43 | @MockBean | 50 | @MockBean |
| 44 | private DynamicDataSourceProperties dynamicDataSourceProperties; | 51 | private DynamicDataSourceProperties dynamicDataSourceProperties; |
| 45 | 52 | ||
| 53 | + @BeforeEach | ||
| 54 | + public void setUp() { | ||
| 55 | + // mock 一个空实现的 StringEncryptor,避免 EncryptTypeHandler 报错 | ||
| 56 | + ReflectUtil.setFieldValue(EncryptTypeHandler.class, "encryptor", stringEncryptor); | ||
| 57 | + when(stringEncryptor.encrypt(anyString())).then((Answer<String>) invocation -> invocation.getArgument(0)); | ||
| 58 | + when(stringEncryptor.decrypt(anyString())).then((Answer<String>) invocation -> invocation.getArgument(0)); | ||
| 59 | + } | ||
| 60 | + | ||
| 46 | @Test | 61 | @Test |
| 47 | public void testCreateDataSourceConfig_success() { | 62 | public void testCreateDataSourceConfig_success() { |
| 48 | try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) { | 63 | try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) { |
| 49 | // 准备参数 | 64 | // 准备参数 |
| 50 | DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class); | 65 | DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class); |
| 51 | // mock 方法 | 66 | // mock 方法 |
| 52 | - when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456"); | ||
| 53 | databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()), | 67 | databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()), |
| 54 | eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); | 68 | eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); |
| 55 | 69 | ||
| @@ -59,8 +73,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | @@ -59,8 +73,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | ||
| 59 | assertNotNull(dataSourceConfigId); | 73 | assertNotNull(dataSourceConfigId); |
| 60 | // 校验记录的属性是否正确 | 74 | // 校验记录的属性是否正确 |
| 61 | DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId); | 75 | DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId); |
| 62 | - assertPojoEquals(reqVO, dataSourceConfig, "password"); | ||
| 63 | - assertEquals("123456", dataSourceConfig.getPassword()); | 76 | + assertPojoEquals(reqVO, dataSourceConfig); |
| 64 | } | 77 | } |
| 65 | } | 78 | } |
| 66 | 79 | ||
| @@ -75,7 +88,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | @@ -75,7 +88,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | ||
| 75 | o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID | 88 | o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID |
| 76 | }); | 89 | }); |
| 77 | // mock 方法 | 90 | // mock 方法 |
| 78 | - when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456"); | 91 | +// when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456"); |
| 79 | databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()), | 92 | databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()), |
| 80 | eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); | 93 | eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); |
| 81 | 94 | ||
| @@ -83,8 +96,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | @@ -83,8 +96,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { | ||
| 83 | dataSourceConfigService.updateDataSourceConfig(reqVO); | 96 | dataSourceConfigService.updateDataSourceConfig(reqVO); |
| 84 | // 校验是否更新正确 | 97 | // 校验是否更新正确 |
| 85 | DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的 | 98 | DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的 |
| 86 | - assertPojoEquals(reqVO, dataSourceConfig, "password"); | ||
| 87 | - assertEquals("123456", dataSourceConfig.getPassword()); | 99 | + assertPojoEquals(reqVO, dataSourceConfig); |
| 88 | } | 100 | } |
| 89 | } | 101 | } |
| 90 | 102 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sensitiveword/SensitiveWordDO.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | -import cn.iocoder.yudao.framework.mybatis.core.type.StringLiSTTypeHandler; | 5 | +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; |
| 6 | import com.baomidou.mybatisplus.annotation.KeySequence; | 6 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 7 | import com.baomidou.mybatisplus.annotation.TableField; | 7 | import com.baomidou.mybatisplus.annotation.TableField; |
| 8 | import com.baomidou.mybatisplus.annotation.TableId; | 8 | import com.baomidou.mybatisplus.annotation.TableId; |
| @@ -46,7 +46,7 @@ public class SensitiveWordDO extends BaseDO { | @@ -46,7 +46,7 @@ public class SensitiveWordDO extends BaseDO { | ||
| 46 | * 例如说,tag 有短信、论坛两种,敏感词 "推广" 在短信下是敏感词,在论坛下不是敏感词。 | 46 | * 例如说,tag 有短信、论坛两种,敏感词 "推广" 在短信下是敏感词,在论坛下不是敏感词。 |
| 47 | * 此时,我们会存储一条敏感词记录,它的 name 为"推广",tag 为短信。 | 47 | * 此时,我们会存储一条敏感词记录,它的 name 为"推广",tag 为短信。 |
| 48 | */ | 48 | */ |
| 49 | - @TableField(typeHandler = StringLiSTTypeHandler.class) | 49 | + @TableField(typeHandler = StringListTypeHandler.class) |
| 50 | private List<String> tags; | 50 | private List<String> tags; |
| 51 | /** | 51 | /** |
| 52 | * 状态 | 52 | * 状态 |
yudao-server/src/main/java/cn/iocoder/yudao/server/framework/tip/core/TipApplicationRunner.java
| @@ -23,7 +23,7 @@ public class TipApplicationRunner implements ApplicationRunner { | @@ -23,7 +23,7 @@ public class TipApplicationRunner implements ApplicationRunner { | ||
| 23 | "项目启动成功!\n\t" + | 23 | "项目启动成功!\n\t" + |
| 24 | "接口文档: \t{} \n\t" + | 24 | "接口文档: \t{} \n\t" + |
| 25 | "开发文档: \t{} \n\t" + | 25 | "开发文档: \t{} \n\t" + |
| 26 | - "视频教程: \t{} \n" + | 26 | + "视频教程: \t{} \n\t" + |
| 27 | "源码解析: \t{} \n" + | 27 | "源码解析: \t{} \n" + |
| 28 | "----------------------------------------------------------", | 28 | "----------------------------------------------------------", |
| 29 | "https://mtw.so/6w48hX", | 29 | "https://mtw.so/6w48hX", |