commit 3a10e8ca53a036921723a1dfc593692c5f447a33
Author: xiaotan <1031040634@qq.com>
Date: Sat Apr 6 00:17:56 2024 +0800
初始化
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b34d8b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+target/
+# Created by .ignore support plugin (hsz.mobi)
+*.iml
+*.iws
+# out
+**/out/
+package-lock.json
+yarn.lock
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..137069b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,73 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ae9ec78
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# bs-jxc
+
+保税进销存
\ No newline at end of file
diff --git a/bin/clean.bat b/bin/clean.bat
new file mode 100644
index 0000000..24c0974
--- /dev/null
+++ b/bin/clean.bat
@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo [Ϣ] target·
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+call mvn clean
+
+pause
\ No newline at end of file
diff --git a/bin/package.bat b/bin/package.bat
new file mode 100644
index 0000000..c693ec0
--- /dev/null
+++ b/bin/package.bat
@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo [Ϣ] Weḅwar/jarļ
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+call mvn clean package -Dmaven.test.skip=true
+
+pause
\ No newline at end of file
diff --git a/bin/run.bat b/bin/run.bat
new file mode 100644
index 0000000..41efbd0
--- /dev/null
+++ b/bin/run.bat
@@ -0,0 +1,14 @@
+@echo off
+echo.
+echo [Ϣ] ʹJarWeb̡
+echo.
+
+cd %~dp0
+cd ../ruoyi-admin/target
+
+set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
+
+java -jar %JAVA_OPTS% ruoyi-admin.jar
+
+cd bin
+pause
\ No newline at end of file
diff --git a/bs-admin/pom.xml b/bs-admin/pom.xml
new file mode 100644
index 0000000..6fe3be0
--- /dev/null
+++ b/bs-admin/pom.xml
@@ -0,0 +1,99 @@
+
+
+
+ my-base
+ com.bs
+ 3.8.6
+
+ 4.0.0
+ jar
+ bs-admin
+
+
+ web服务入口
+
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+ io.springfox
+ springfox-boot-starter
+
+
+
+
+ io.swagger
+ swagger-models
+ 1.6.2
+
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+
+ com.bs
+ bs-framework
+
+
+
+
+ com.bs
+ bs-quartz
+
+
+
+
+ com.bs
+ bs-generator
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.1.1.RELEASE
+
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 3.1.0
+
+ false
+ ${project.artifactId}
+
+
+
+ ${project.artifactId}
+
+
+
diff --git a/bs-admin/src/main/java/com/bs/BsApplication.java b/bs-admin/src/main/java/com/bs/BsApplication.java
new file mode 100644
index 0000000..a1302fe
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/BsApplication.java
@@ -0,0 +1,23 @@
+package com.bs;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+/**
+ * 启动程序
+ *
+ * @author bs
+ */
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
+@EnableConfigurationProperties
+public class BsApplication
+{
+ public static void main(String[] args)
+ {
+ // System.setProperty("spring.devtools.restart.enabled", "false");
+ SpringApplication.run(BsApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 启动成功 ლ(´ڡ`ლ)゙ ");
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/BsServletInitializer.java b/bs-admin/src/main/java/com/bs/BsServletInitializer.java
new file mode 100644
index 0000000..d9439e7
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/BsServletInitializer.java
@@ -0,0 +1,18 @@
+package com.bs;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+/**
+ * web容器中进行部署
+ *
+ * @author bs
+ */
+public class BsServletInitializer extends SpringBootServletInitializer
+{
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
+ {
+ return application.sources(BsApplication.class);
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/cm/controller/CmAttachController.java b/bs-admin/src/main/java/com/bs/cm/controller/CmAttachController.java
new file mode 100644
index 0000000..17252cf
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/cm/controller/CmAttachController.java
@@ -0,0 +1,208 @@
+package com.bs.cm.controller;
+
+import cn.hutool.core.lang.Validator;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.cm.domain.CmAttach;
+import com.bs.cm.service.ICmAttachService;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 附件信息Controller
+ *
+ * @author bs
+ * @date 2024-03-03
+ */
+@Api(tags = "附件信息")
+@RestController
+@RequestMapping("/cm/attach")
+public class CmAttachController extends BaseController {
+ @Resource
+ private ICmAttachService cmAttachService;
+
+ /**
+ * 分页查询附件信息列表
+ */
+ @ApiOperation("分页查询附件信息列表")
+ @GetMapping("/pageList")
+ public TableDataInfo pageList(CmAttach cmAttach) {
+ startPage();
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, cmAttach);
+ List list = cmAttachService.list(queryWrapper);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询附件信息列表
+ */
+ @ApiOperation("查询附件信息列表")
+ @GetMapping("/list")
+ public AjaxResult list(CmAttach cmAttach) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, cmAttach);
+ List list = cmAttachService.list(queryWrapper);
+ return success(list);
+ }
+
+ /**
+ * 导出附件信息列表
+ */
+ @ApiOperation("导出附件信息列表")
+ @Log(title = "附件信息导出", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, CmAttach cmAttach) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, cmAttach);
+ List list = cmAttachService.list(queryWrapper);
+ ExcelUtil util = new ExcelUtil(CmAttach.class);
+ util.exportExcel(response, list, "附件信息数据");
+ }
+
+ /**
+ * 获取附件信息详细信息
+ */
+ @ApiOperation("获取附件信息详细信息")
+ @GetMapping(value = "/{id}")
+ public AjaxResult getInfo(@PathVariable("id") Long id) {
+ return success(cmAttachService.getById(id));
+ }
+
+ /**
+ * 新增附件信息
+ */
+ @ApiOperation("新增附件信息")
+ @Log(title = "附件信息新增", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody CmAttach cmAttach) {
+ return toAjax(cmAttachService.save(cmAttach));
+ }
+
+ /**
+ * 修改附件信息
+ */
+ @ApiOperation("修改附件信息")
+ @Log(title = "附件信息修改", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody CmAttach cmAttach) {
+ return toAjax(cmAttachService.updateById(cmAttach));
+ }
+
+ /**
+ * 删除附件信息
+ */
+ @ApiOperation("删除附件信息")
+ @Log(title = "附件信息删除", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public AjaxResult remove(@PathVariable List ids) {
+ return toAjax(cmAttachService.removeBatchByIds(ids));
+ }
+
+
+ /**
+ * 条件设置
+ */
+ private void condition(LambdaQueryWrapper queryWrapper, CmAttach cmAttach) {
+ //用户id
+ if (Validator.isNotEmpty(cmAttach.getId())) {
+ queryWrapper.eq(CmAttach::getId, cmAttach.getId());
+ }
+
+ //文件组id
+ if (Validator.isNotEmpty(cmAttach.getFileId())) {
+ queryWrapper.eq(CmAttach::getFileId, cmAttach.getFileId());
+ }
+
+ //文件排序
+ if (Validator.isNotEmpty(cmAttach.getFileSort())) {
+ queryWrapper.eq(CmAttach::getFileSort, cmAttach.getFileSort());
+ }
+
+ //文件名称(编译后)
+ if (Validator.isNotEmpty(cmAttach.getAttachName())) {
+ queryWrapper.eq(CmAttach::getAttachName, cmAttach.getAttachName());
+ }
+
+ //文件类型
+ if (Validator.isNotEmpty(cmAttach.getAttachFileType())) {
+ queryWrapper.eq(CmAttach::getAttachFileType, cmAttach.getAttachFileType());
+ }
+
+ //文件编码类型
+ if (Validator.isNotEmpty(cmAttach.getAttachContentType())) {
+ queryWrapper.eq(CmAttach::getAttachContentType, cmAttach.getAttachContentType());
+ }
+
+ //文件大小
+ if (Validator.isNotEmpty(cmAttach.getAttachFileSize())) {
+ queryWrapper.eq(CmAttach::getAttachFileSize, cmAttach.getAttachFileSize());
+ }
+
+ //文件路径
+ if (Validator.isNotEmpty(cmAttach.getAttachFileUrl())) {
+ queryWrapper.eq(CmAttach::getAttachFileUrl, cmAttach.getAttachFileUrl());
+ }
+
+ //文件名(原始)
+ if (Validator.isNotEmpty(cmAttach.getOldName())) {
+ queryWrapper.eq(CmAttach::getOldName, cmAttach.getOldName());
+ }
+
+ //文件版本号
+ if (Validator.isNotEmpty(cmAttach.getVersionNo())) {
+ queryWrapper.eq(CmAttach::getVersionNo, cmAttach.getVersionNo());
+ }
+
+ //创建部门
+ if (Validator.isNotEmpty(cmAttach.getCreateDept())) {
+ queryWrapper.eq(CmAttach::getCreateDept, cmAttach.getCreateDept());
+ }
+
+ //创建人
+ if (Validator.isNotEmpty(cmAttach.getCreateBy())) {
+ queryWrapper.eq(CmAttach::getCreateBy, cmAttach.getCreateBy());
+ }
+
+ //创建时间
+ if (Validator.isNotEmpty(cmAttach.getCreateTime())) {
+ queryWrapper.eq(CmAttach::getCreateTime, cmAttach.getCreateTime());
+ }
+
+ //修改人
+ if (Validator.isNotEmpty(cmAttach.getUpdateBy())) {
+ queryWrapper.eq(CmAttach::getUpdateBy, cmAttach.getUpdateBy());
+ }
+
+ //修改时间
+ if (Validator.isNotEmpty(cmAttach.getUpdateTime())) {
+ queryWrapper.eq(CmAttach::getUpdateTime, cmAttach.getUpdateTime());
+ }
+
+ //备注
+ if (Validator.isNotEmpty(cmAttach.getRemark())) {
+ queryWrapper.eq(CmAttach::getRemark, cmAttach.getRemark());
+ }
+
+ //删除标志(0代表存在 2代表删除)
+ if (Validator.isNotEmpty(cmAttach.getDelFlag())) {
+ queryWrapper.eq(CmAttach::getDelFlag, cmAttach.getDelFlag());
+ }
+
+ //${column.columnComment}
+ if (Validator.isNotEmpty(cmAttach.getTenantId())) {
+ queryWrapper.eq(CmAttach::getTenantId, cmAttach.getTenantId());
+ }
+
+ }
+
+}
diff --git a/bs-admin/src/main/java/com/bs/cm/domain/CmAttach.java b/bs-admin/src/main/java/com/bs/cm/domain/CmAttach.java
new file mode 100644
index 0000000..f600da3
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/cm/domain/CmAttach.java
@@ -0,0 +1,94 @@
+package com.bs.cm.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.bs.common.annotation.Excel;
+import com.bs.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * 附件信息对象 cm_attach
+ *
+ * @author bs
+ * @date 2024-03-03
+ */
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("cm_attach")
+@Data
+public class CmAttach extends BaseEntity{
+ private static final long serialVersionUID = 1L;
+
+ /** 用户id */
+ @TableId(value = "id",type = IdType.AUTO)
+ @ApiModelProperty(value = "用户id")
+ private Long id;
+
+ /** 文件组id */
+
+ @Excel(name = "文件组id")
+ @ApiModelProperty(value = "文件组id")
+ private String fileId;
+
+ /** 文件排序 */
+
+ @Excel(name = "文件排序")
+ @ApiModelProperty(value = "文件排序")
+ private Integer fileSort;
+
+ /** 文件名称(编译后) */
+
+ @Excel(name = "文件名称", readConverterExp = "编=译后")
+ @ApiModelProperty(value = "文件名称(编译后)")
+ private String attachName;
+
+ /** 文件类型 */
+
+ @Excel(name = "文件类型")
+ @ApiModelProperty(value = "文件类型")
+ private String attachFileType;
+
+ /** 文件编码类型 */
+
+ @Excel(name = "文件编码类型")
+ @ApiModelProperty(value = "文件编码类型")
+ private String attachContentType;
+
+ /** 文件大小 */
+
+ @Excel(name = "文件大小")
+ @ApiModelProperty(value = "文件大小")
+ private Long attachFileSize;
+
+ /** 文件路径 */
+
+ @Excel(name = "文件路径")
+ @ApiModelProperty(value = "文件路径")
+ private String attachFileUrl;
+
+ /** 文件名(原始) */
+
+ @Excel(name = "文件名", readConverterExp = "原=始")
+ @ApiModelProperty(value = "文件名(原始)")
+ private String oldName;
+
+ /** 文件版本号 */
+
+ @Excel(name = "文件版本号")
+ @ApiModelProperty(value = "文件版本号")
+ private String versionNo;
+
+ /** 备注 */
+
+ @Excel(name = "备注")
+ @ApiModelProperty(value = "备注")
+ private String remark;
+
+
+
+
+}
diff --git a/bs-admin/src/main/java/com/bs/cm/mapper/CmAttachMapper.java b/bs-admin/src/main/java/com/bs/cm/mapper/CmAttachMapper.java
new file mode 100644
index 0000000..d607342
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/cm/mapper/CmAttachMapper.java
@@ -0,0 +1,14 @@
+package com.bs.cm.mapper;
+
+import com.bs.common.mybatis.mapper.BaseMapperX;
+import com.bs.cm.domain.CmAttach;
+
+/**
+ * 附件信息Mapper接口
+ *
+ * @author bs
+ * @date 2024-03-03
+ */
+public interface CmAttachMapper extends BaseMapperX {
+
+}
diff --git a/bs-admin/src/main/java/com/bs/cm/service/ICmAttachService.java b/bs-admin/src/main/java/com/bs/cm/service/ICmAttachService.java
new file mode 100644
index 0000000..c387dd4
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/cm/service/ICmAttachService.java
@@ -0,0 +1,15 @@
+package com.bs.cm.service;
+
+import com.bs.cm.domain.CmAttach;
+import com.bs.common.core.domain.AjaxResult;
+import com.github.yulichang.base.MPJBaseService;
+
+/**
+ * 附件信息Service接口
+ *
+ * @author bs
+ * @date 2024-03-03
+ */
+public interface ICmAttachService extends MPJBaseService{
+
+}
diff --git a/bs-admin/src/main/java/com/bs/cm/service/impl/CmAttachServiceImpl.java b/bs-admin/src/main/java/com/bs/cm/service/impl/CmAttachServiceImpl.java
new file mode 100644
index 0000000..1c83609
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/cm/service/impl/CmAttachServiceImpl.java
@@ -0,0 +1,19 @@
+package com.bs.cm.service.impl;
+
+import com.bs.cm.mapper.CmAttachMapper;
+import com.bs.cm.domain.CmAttach;
+import com.bs.cm.service.ICmAttachService;
+import com.github.yulichang.base.MPJBaseServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+/**
+ * 附件信息Service业务层处理
+ *
+ * @author bs
+ * @date 2024-03-03
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class CmAttachServiceImpl extends MPJBaseServiceImpl implements ICmAttachService {
+
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/common/CaptchaController.java b/bs-admin/src/main/java/com/bs/web/controller/common/CaptchaController.java
new file mode 100644
index 0000000..8b0d937
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/common/CaptchaController.java
@@ -0,0 +1,94 @@
+package com.bs.web.controller.common;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.FastByteArrayOutputStream;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.google.code.kaptcha.Producer;
+import com.bs.common.config.BsConfig;
+import com.bs.common.constant.CacheConstants;
+import com.bs.common.constant.Constants;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.redis.RedisCache;
+import com.bs.common.utils.sign.Base64;
+import com.bs.common.utils.uuid.IdUtils;
+import com.bs.system.service.ISysConfigService;
+
+/**
+ * 验证码操作处理
+ *
+ * @author bs
+ */
+@RestController
+public class CaptchaController
+{
+ @Resource(name = "captchaProducer")
+ private Producer captchaProducer;
+
+ @Resource(name = "captchaProducerMath")
+ private Producer captchaProducerMath;
+
+ @Autowired
+ private RedisCache redisCache;
+
+ @Autowired
+ private ISysConfigService configService;
+ /**
+ * 生成验证码
+ */
+ @GetMapping("/captchaImage")
+ public AjaxResult getCode(HttpServletResponse response) throws IOException
+ {
+ AjaxResult ajax = AjaxResult.success();
+ boolean captchaEnabled = configService.selectCaptchaEnabled();
+ ajax.put("captchaEnabled", captchaEnabled);
+ if (!captchaEnabled)
+ {
+ return ajax;
+ }
+
+ // 保存验证码信息
+ String uuid = IdUtils.simpleUUID();
+ String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
+
+ String capStr = null, code = null;
+ BufferedImage image = null;
+
+ // 生成验证码
+ String captchaType = BsConfig.getCaptchaType();
+ if ("math".equals(captchaType))
+ {
+ String capText = captchaProducerMath.createText();
+ capStr = capText.substring(0, capText.lastIndexOf("@"));
+ code = capText.substring(capText.lastIndexOf("@") + 1);
+ image = captchaProducerMath.createImage(capStr);
+ }
+ else if ("char".equals(captchaType))
+ {
+ capStr = code = captchaProducer.createText();
+ image = captchaProducer.createImage(capStr);
+ }
+
+ redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
+ // 转换流信息写出
+ FastByteArrayOutputStream os = new FastByteArrayOutputStream();
+ try
+ {
+ ImageIO.write(image, "jpg", os);
+ }
+ catch (IOException e)
+ {
+ return AjaxResult.error(e.getMessage());
+ }
+
+ ajax.put("uuid", uuid);
+ ajax.put("img", Base64.encode(os.toByteArray()));
+ return ajax;
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/common/CommonController.java b/bs-admin/src/main/java/com/bs/web/controller/common/CommonController.java
new file mode 100644
index 0000000..5257a2b
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/common/CommonController.java
@@ -0,0 +1,294 @@
+package com.bs.web.controller.common;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.cm.domain.CmAttach;
+import com.bs.cm.service.ICmAttachService;
+import com.bs.common.config.BsConfig;
+import com.bs.common.constant.Constants;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.exception.ServiceException;
+import com.bs.common.utils.FileUtil;
+import com.bs.common.utils.StringUtils;
+import com.bs.common.utils.file.FileUploadUtils;
+import com.bs.common.utils.file.FileUtils;
+import com.bs.framework.config.ServerConfig;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 通用请求处理
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/common")
+@Api(tags = "文件上传")
+public class CommonController {
+ private static final Logger log = LoggerFactory.getLogger(CommonController.class);
+
+ @Autowired
+ private ServerConfig serverConfig;
+
+ @Autowired
+ private ICmAttachService cmAttachService;
+
+ private static final String FILE_DELIMETER = ",";
+
+ /**
+ * 通用下载请求
+ *
+ * @param fileName 文件名称
+ * @param delete 是否删除
+ */
+ @GetMapping("/download")
+ @ApiOperation("文件下载")
+ public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) {
+ try {
+ if (!FileUtils.checkAllowDownload(fileName)) {
+ throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
+ }
+ String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+ String filePath = BsConfig.getDownloadPath() + fileName;
+
+ response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+ FileUtils.setAttachmentResponseHeader(response, realFileName);
+ FileUtils.writeBytes(filePath, response.getOutputStream());
+ if (delete) {
+ FileUtils.deleteFile(filePath);
+ }
+ } catch (Exception e) {
+ log.error("下载文件失败", e);
+ }
+ }
+
+
+ @ApiOperation("根据attachId下载对应文件")
+ @GetMapping("/fileDownloadById")
+ public void fileDownloadById(String attachId,HttpServletResponse response, HttpServletRequest request) {
+ if (StringUtils.isBlank(attachId) || "null".equals(attachId)) {
+ throw new ServiceException("附件ID不能为空");
+ }
+ CmAttach cmAttach = cmAttachService.getById(attachId);
+ String path =cmAttach.getAttachFileUrl();
+ path =BsConfig.getFilePath()+path;
+// Constants.RESOURCE_PREFIX
+ path = path.replace(Constants.RESOURCE_PREFIX, "");
+ File compressedImageFile = new File(path);
+ response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+ FileUtil.downloadFile(request, response, compressedImageFile, false);
+ }
+
+
+
+ /**
+ * 通用上传请求(单个)
+ */
+ @PostMapping("/upload")
+ @ApiOperation("单文件上传")
+ public AjaxResult uploadFile(MultipartFile file,String fileId) throws Exception {
+ try {
+ // 上传文件路径
+ String filePath = BsConfig.getUploadPath();
+ // 上传并返回新文件名称
+ String fileName = FileUploadUtils.upload(filePath, file);
+ String url = serverConfig.getUrl() + fileName;
+ String newFileName = FileUtils.getName(fileName);
+ String originalFilename = file.getOriginalFilename();
+ CmAttach cmAttach = new CmAttach();
+ cmAttach.setFileId(fileId);
+ cmAttach.setFileSort(1);
+ cmAttach.setAttachName(newFileName);
+ cmAttach.setAttachContentType(file.getContentType());
+ cmAttach.setAttachFileSize(file.getSize());
+ String suffix = FileUtil.getExtensionName(originalFilename);
+ String type = FileUtil.getFileType(suffix);
+ cmAttach.setAttachFileType(type);
+ cmAttach.setAttachFileUrl(fileName);
+ cmAttach.setOldName(originalFilename);
+ cmAttach.setVersionNo("1");
+ cmAttach.setRemark("");
+ cmAttachService.save(cmAttach);
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("url", url);
+ ajax.put("fileName", fileName);
+ ajax.put("newFileName", newFileName);
+ ajax.put("cmAttach", cmAttach);
+ ajax.put("originalFilename", originalFilename);
+ return ajax;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error(e.getMessage());
+ }
+ }
+
+ /**
+ * 通用上传请求(多个)
+ */
+ @PostMapping("/uploads")
+ @ApiOperation("多文件上传")
+ public AjaxResult uploadFiles(List files, String fileId) throws Exception {
+ try {
+ // 上传文件路径
+ String filePath = BsConfig.getUploadPath();
+ List urls = new ArrayList();
+ List fileNames = new ArrayList();
+ List newFileNames = new ArrayList();
+ List originalFilenames = new ArrayList();
+ List cmAttachList = new ArrayList<>();
+ for (MultipartFile file : files) {
+ // 上传并返回新文件名称
+ String fileName = FileUploadUtils.upload(filePath, file);
+ String url = serverConfig.getUrl() + fileName;
+ urls.add(url);
+ fileNames.add(fileName);
+ String newFileName = FileUtils.getName(fileName);
+ newFileNames.add(newFileName);
+ String originalFilename = file.getOriginalFilename();
+ originalFilenames.add(originalFilename);
+ CmAttach cmAttach = new CmAttach();
+ cmAttach.setFileId(fileId);
+ cmAttach.setFileSort(1);
+ cmAttach.setAttachName(newFileName);
+ cmAttach.setAttachContentType(file.getContentType());
+ cmAttach.setAttachFileSize(file.getSize());
+ String suffix = FileUtil.getExtensionName(originalFilename);
+ String type = FileUtil.getFileType(suffix);
+ cmAttach.setAttachFileType(type);
+ cmAttach.setAttachFileUrl(fileName);
+ cmAttach.setOldName(originalFilename);
+ cmAttach.setVersionNo("1");
+ cmAttach.setRemark("");
+ cmAttachList.add(cmAttach);
+ }
+ cmAttachService.saveBatch(cmAttachList);
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
+ ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
+ ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
+ ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
+ return ajax;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error(e.getMessage());
+ }
+ }
+
+ /**
+ * 上查询文件列表
+ * @param fileId
+ * @param type
+ * @return
+ */
+ @ApiOperation("根据fileId查找附件列表")
+ @GetMapping(value = "/getAttachList/{fileId}")
+ public AjaxResult getAttachVo(@PathVariable String fileId, String type) {
+ if (org.apache.commons.lang3.StringUtils.isBlank(fileId) || "null".equals(fileId)) {
+ return AjaxResult.error("fileId不能为空");
+ } else if ("undefined".equals(fileId)) {
+ return AjaxResult.success();
+ }
+ List attachVo = cmAttachService.list(new LambdaQueryWrapper().eq(CmAttach::getFileId,fileId));
+ //压缩图片
+ return AjaxResult.success(attachVo);
+ }
+
+ /**
+ * 根据fileIds查找附件列表
+ * @param fileIds
+ * @return
+ */
+ @ApiOperation("根据fileIds查找附件列表")
+ @GetMapping(value = "/getFileIdsByAttachVos")
+ public AjaxResult getFileIdsByAttachVos(String[] fileIds) {
+ LambdaQueryWrapper refWrapper = new LambdaQueryWrapper();
+ if (null != fileIds && fileIds.length > 0) {
+ refWrapper.in(CmAttach::getFileId, fileIds);
+ List refs = cmAttachService.list(refWrapper);
+ //通过fileId进行分组
+ Map> attachVoMap = refs.stream().collect(Collectors.groupingBy(CmAttach::getFileId));
+ return AjaxResult.success(attachVoMap);
+ } else {
+ return AjaxResult.warn("请输入文件fileIds!");
+ }
+ }
+
+
+ @ApiOperation("根据fileId删除表及对应文件")
+ @DeleteMapping(value = "/deleteFileByFileId/{fileId}")
+ public AjaxResult deleteFileByFileId(@PathVariable String fileId) {
+ if (org.apache.commons.lang3.StringUtils.isBlank(fileId) || "null".equals(fileId)) {
+ return AjaxResult.error("fileId不能为空");
+ }
+ List cmAttachs = cmAttachService.list(new LambdaQueryWrapper().eq(CmAttach::getFileId,fileId));
+ cmAttachs.forEach(cmAttach->{
+ String path =cmAttach.getAttachFileUrl();
+ /* path = path.replace(Constants.RESOURCE_PREFIX, "");
+ path = path.replace(cmAttach.getAttachName(), "");
+ path =BsConfig.getFilePath()+path+"xt_"+cmAttach.getAttachName();*/
+ path =BsConfig.getFilePath()+path;
+ File compressedImageFile = new File(path);
+ if (compressedImageFile.exists()) {
+ FileUtil.del(compressedImageFile);
+ }
+ });
+ cmAttachService.remove(new LambdaQueryWrapper().eq(CmAttach::getFileId,fileId));
+ return AjaxResult.success();
+ }
+
+
+ @ApiOperation("根据attachId删除表及对应文件")
+ @DeleteMapping(value = "/deleteFile/{attachId}")
+ public AjaxResult deleteFile(@PathVariable Long attachId) {
+ if (attachId==null) {
+ return AjaxResult.error("attachId不能为空");
+ }
+ CmAttach cmAttach = cmAttachService.getById(attachId);
+ String path =cmAttach.getAttachFileUrl();
+ path =BsConfig.getFilePath()+path;
+ File compressedImageFile = new File(path);
+ if (compressedImageFile.exists()) {
+ FileUtil.del(compressedImageFile);
+ }
+ cmAttachService.removeById(attachId);
+ return AjaxResult.success();
+ }
+
+
+ /**
+ * 本地资源通用下载
+ */
+ @GetMapping("/download/resource")
+ public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
+ throws Exception {
+ try {
+ if (!FileUtils.checkAllowDownload(resource)) {
+ throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
+ }
+ // 本地资源路径
+ String localPath = BsConfig.getProfile();
+ // 数据库资源地址
+ String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
+ // 下载名称
+ String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
+ response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+ FileUtils.setAttachmentResponseHeader(response, downloadName);
+ FileUtils.writeBytes(downloadPath, response.getOutputStream());
+ } catch (Exception e) {
+ log.error("下载文件失败", e);
+ }
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/monitor/CacheController.java b/bs-admin/src/main/java/com/bs/web/controller/monitor/CacheController.java
new file mode 100644
index 0000000..5de9cf5
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/monitor/CacheController.java
@@ -0,0 +1,120 @@
+package com.bs.web.controller.monitor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.constant.CacheConstants;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.utils.StringUtils;
+import com.bs.system.domain.SysCache;
+
+/**
+ * 缓存监控
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/monitor/cache")
+public class CacheController
+{
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ private final static List caches = new ArrayList();
+ {
+ caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
+ caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
+ caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
+ caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
+ caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
+ caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
+ caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @GetMapping()
+ public AjaxResult getInfo() throws Exception
+ {
+ Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info());
+ Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats"));
+ Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize());
+
+ Map result = new HashMap<>(3);
+ result.put("info", info);
+ result.put("dbSize", dbSize);
+
+ List> pieList = new ArrayList<>();
+ commandStats.stringPropertyNames().forEach(key -> {
+ Map data = new HashMap<>(2);
+ String property = commandStats.getProperty(key);
+ data.put("name", StringUtils.removeStart(key, "cmdstat_"));
+ data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
+ pieList.add(data);
+ });
+ result.put("commandStats", pieList);
+ return AjaxResult.success(result);
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @GetMapping("/getNames")
+ public AjaxResult cache()
+ {
+ return AjaxResult.success(caches);
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @GetMapping("/getKeys/{cacheName}")
+ public AjaxResult getCacheKeys(@PathVariable String cacheName)
+ {
+ Set cacheKeys = redisTemplate.keys(cacheName + "*");
+ return AjaxResult.success(cacheKeys);
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @GetMapping("/getValue/{cacheName}/{cacheKey}")
+ public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey)
+ {
+ String cacheValue = redisTemplate.opsForValue().get(cacheKey);
+ SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
+ return AjaxResult.success(sysCache);
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @DeleteMapping("/clearCacheName/{cacheName}")
+ public AjaxResult clearCacheName(@PathVariable String cacheName)
+ {
+ Collection cacheKeys = redisTemplate.keys(cacheName + "*");
+ redisTemplate.delete(cacheKeys);
+ return AjaxResult.success();
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @DeleteMapping("/clearCacheKey/{cacheKey}")
+ public AjaxResult clearCacheKey(@PathVariable String cacheKey)
+ {
+ redisTemplate.delete(cacheKey);
+ return AjaxResult.success();
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
+ @DeleteMapping("/clearCacheAll")
+ public AjaxResult clearCacheAll()
+ {
+ Collection cacheKeys = redisTemplate.keys("*");
+ redisTemplate.delete(cacheKeys);
+ return AjaxResult.success();
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/monitor/ServerController.java b/bs-admin/src/main/java/com/bs/web/controller/monitor/ServerController.java
new file mode 100644
index 0000000..2d878de
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/monitor/ServerController.java
@@ -0,0 +1,27 @@
+package com.bs.web.controller.monitor;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.framework.web.domain.Server;
+
+/**
+ * 服务器监控
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/monitor/server")
+public class ServerController
+{
+ @PreAuthorize("@ss.hasPermi('monitor:server:list')")
+ @GetMapping()
+ public AjaxResult getInfo() throws Exception
+ {
+ Server server = new Server();
+ server.copyTo();
+ return AjaxResult.success(server);
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/monitor/SysLogininforController.java b/bs-admin/src/main/java/com/bs/web/controller/monitor/SysLogininforController.java
new file mode 100644
index 0000000..56063c6
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/monitor/SysLogininforController.java
@@ -0,0 +1,82 @@
+package com.bs.web.controller.monitor;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.framework.web.service.SysPasswordService;
+import com.bs.system.domain.SysLogininfor;
+import com.bs.system.service.ISysLogininforService;
+
+/**
+ * 系统访问记录
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/monitor/logininfor")
+public class SysLogininforController extends BaseController
+{
+ @Autowired
+ private ISysLogininforService logininforService;
+
+ @Autowired
+ private SysPasswordService passwordService;
+
+ @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysLogininfor logininfor)
+ {
+ startPage();
+ List list = logininforService.selectLogininforList(logininfor);
+ return getDataTable(list);
+ }
+
+ @Log(title = "登录日志", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysLogininfor logininfor)
+ {
+ List list = logininforService.selectLogininforList(logininfor);
+ ExcelUtil util = new ExcelUtil(SysLogininfor.class);
+ util.exportExcel(response, list, "登录日志");
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
+ @Log(title = "登录日志", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{infoIds}")
+ public AjaxResult remove(@PathVariable Long[] infoIds)
+ {
+ return toAjax(logininforService.deleteLogininforByIds(infoIds));
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
+ @Log(title = "登录日志", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ logininforService.cleanLogininfor();
+ return success();
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
+ @Log(title = "账户解锁", businessType = BusinessType.OTHER)
+ @GetMapping("/unlock/{userName}")
+ public AjaxResult unlock(@PathVariable("userName") String userName)
+ {
+ passwordService.clearLoginRecordCache(userName);
+ return success();
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/monitor/SysOperlogController.java b/bs-admin/src/main/java/com/bs/web/controller/monitor/SysOperlogController.java
new file mode 100644
index 0000000..b00fa95
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/monitor/SysOperlogController.java
@@ -0,0 +1,69 @@
+package com.bs.web.controller.monitor;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.domain.SysOperLog;
+import com.bs.system.service.ISysOperLogService;
+
+/**
+ * 操作日志记录
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/monitor/operlog")
+public class SysOperlogController extends BaseController
+{
+ @Autowired
+ private ISysOperLogService operLogService;
+
+ @PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysOperLog operLog)
+ {
+ startPage();
+ List list = operLogService.selectOperLogList(operLog);
+ return getDataTable(list);
+ }
+
+ @Log(title = "操作日志", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysOperLog operLog)
+ {
+ List list = operLogService.selectOperLogList(operLog);
+ ExcelUtil util = new ExcelUtil(SysOperLog.class);
+ util.exportExcel(response, list, "操作日志");
+ }
+
+ @Log(title = "操作日志", businessType = BusinessType.DELETE)
+ @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
+ @DeleteMapping("/{operIds}")
+ public AjaxResult remove(@PathVariable Long[] operIds)
+ {
+ return toAjax(operLogService.deleteOperLogByIds(operIds));
+ }
+
+ @Log(title = "操作日志", businessType = BusinessType.CLEAN)
+ @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ operLogService.cleanOperLog();
+ return success();
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/monitor/SysUserOnlineController.java b/bs-admin/src/main/java/com/bs/web/controller/monitor/SysUserOnlineController.java
new file mode 100644
index 0000000..9a2bfa9
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/monitor/SysUserOnlineController.java
@@ -0,0 +1,81 @@
+package com.bs.web.controller.monitor;
+
+import cn.hutool.core.lang.Validator;
+import com.bs.common.annotation.Log;
+import com.bs.common.constant.CacheConstants;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.model.LoginUser;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.core.redis.RedisCache;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.StringUtils;
+import com.bs.system.domain.SysUserOnline;
+import com.bs.system.service.ISysUserOnlineService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 在线用户监控
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/monitor/online")
+public class SysUserOnlineController extends BaseController
+{
+ @Autowired
+ private ISysUserOnlineService userOnlineService;
+
+ @Autowired
+ private RedisCache redisCache;
+
+ @PreAuthorize("@ss.hasPermi('monitor:online:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(String ipaddr, String userName)
+ {
+ Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
+ List userOnlineList = new ArrayList();
+ for (String key : keys)
+ {
+ LoginUser user = redisCache.getCacheObject(key);
+ if (Validator.isNotEmpty(ipaddr) && Validator.isNotEmpty(userName))
+ {
+ userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
+ }
+ else if (Validator.isNotEmpty(ipaddr))
+ {
+ userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
+ }
+ else if (Validator.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
+ {
+ userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
+ }
+ else
+ {
+ userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
+ }
+ }
+ Collections.reverse(userOnlineList);
+ userOnlineList.removeAll(Collections.singleton(null));
+ return getDataTable(userOnlineList);
+ }
+
+ /**
+ * 强退用户
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
+ @Log(title = "在线用户", businessType = BusinessType.FORCE)
+ @DeleteMapping("/{tokenId}")
+ public AjaxResult forceLogout(@PathVariable String tokenId)
+ {
+ redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
+ return success();
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysConfigController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysConfigController.java
new file mode 100644
index 0000000..5ec6cae
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysConfigController.java
@@ -0,0 +1,133 @@
+package com.bs.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.domain.SysConfig;
+import com.bs.system.service.ISysConfigService;
+
+/**
+ * 参数配置 信息操作处理
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/config")
+public class SysConfigController extends BaseController
+{
+ @Autowired
+ private ISysConfigService configService;
+
+ /**
+ * 获取参数配置列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:config:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysConfig config)
+ {
+ startPage();
+ List list = configService.selectConfigList(config);
+ return getDataTable(list);
+ }
+
+ @Log(title = "参数管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:config:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysConfig config)
+ {
+ List list = configService.selectConfigList(config);
+ ExcelUtil util = new ExcelUtil(SysConfig.class);
+ util.exportExcel(response, list, "参数数据");
+ }
+
+ /**
+ * 根据参数编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:config:query')")
+ @GetMapping(value = "/{configId}")
+ public AjaxResult getInfo(@PathVariable Long configId)
+ {
+ return success(configService.selectConfigById(configId));
+ }
+
+ /**
+ * 根据参数键名查询参数值
+ */
+ @GetMapping(value = "/configKey/{configKey}")
+ public AjaxResult getConfigKey(@PathVariable String configKey)
+ {
+ return success(configService.selectConfigByKey(configKey));
+ }
+
+ /**
+ * 新增参数配置
+ */
+ @PreAuthorize("@ss.hasPermi('system:config:add')")
+ @Log(title = "参数管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysConfig config)
+ {
+ if (!configService.checkConfigKeyUnique(config))
+ {
+ return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
+ }
+ config.setCreateBy(getUsername());
+ return toAjax(configService.insertConfig(config));
+ }
+
+ /**
+ * 修改参数配置
+ */
+ @PreAuthorize("@ss.hasPermi('system:config:edit')")
+ @Log(title = "参数管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysConfig config)
+ {
+ if (!configService.checkConfigKeyUnique(config))
+ {
+ return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
+ }
+ config.setUpdateBy(getUsername());
+ return toAjax(configService.updateConfig(config));
+ }
+
+ /**
+ * 删除参数配置
+ */
+ @PreAuthorize("@ss.hasPermi('system:config:remove')")
+ @Log(title = "参数管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{configIds}")
+ public AjaxResult remove(@PathVariable Long[] configIds)
+ {
+ configService.deleteConfigByIds(configIds);
+ return success();
+ }
+
+ /**
+ * 刷新参数缓存
+ */
+ @PreAuthorize("@ss.hasPermi('system:config:remove')")
+ @Log(title = "参数管理", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/refreshCache")
+ public AjaxResult refreshCache()
+ {
+ configService.resetConfigCache();
+ return success();
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysDeptController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysDeptController.java
new file mode 100644
index 0000000..bf15568
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysDeptController.java
@@ -0,0 +1,132 @@
+package com.bs.web.controller.system;
+
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.constant.UserConstants;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysDept;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.StringUtils;
+import com.bs.system.service.ISysDeptService;
+
+/**
+ * 部门信息
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/dept")
+public class SysDeptController extends BaseController
+{
+ @Autowired
+ private ISysDeptService deptService;
+
+ /**
+ * 获取部门列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:list')")
+ @GetMapping("/list")
+ public AjaxResult list(SysDept dept)
+ {
+ List depts = deptService.selectDeptList(dept);
+ return success(depts);
+ }
+
+ /**
+ * 查询部门列表(排除节点)
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:list')")
+ @GetMapping("/list/exclude/{deptId}")
+ public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
+ {
+ List depts = deptService.selectDeptList(new SysDept());
+ depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
+ return success(depts);
+ }
+
+ /**
+ * 根据部门编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:query')")
+ @GetMapping(value = "/{deptId}")
+ public AjaxResult getInfo(@PathVariable Long deptId)
+ {
+ deptService.checkDeptDataScope(deptId);
+ return success(deptService.selectDeptById(deptId));
+ }
+
+ /**
+ * 新增部门
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:add')")
+ @Log(title = "部门管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysDept dept)
+ {
+ if (!deptService.checkDeptNameUnique(dept))
+ {
+ return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
+ }
+ dept.setCreateBy(getUsername());
+ return toAjax(deptService.insertDept(dept));
+ }
+
+ /**
+ * 修改部门
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:edit')")
+ @Log(title = "部门管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysDept dept)
+ {
+ Long deptId = dept.getDeptId();
+ deptService.checkDeptDataScope(deptId);
+ if (!deptService.checkDeptNameUnique(dept))
+ {
+ return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
+ }
+ else if (dept.getParentId().equals(deptId))
+ {
+ return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
+ }
+ else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
+ {
+ return error("该部门包含未停用的子部门!");
+ }
+ dept.setUpdateBy(getUsername());
+ return toAjax(deptService.updateDept(dept));
+ }
+
+ /**
+ * 删除部门
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:remove')")
+ @Log(title = "部门管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{deptId}")
+ public AjaxResult remove(@PathVariable Long deptId)
+ {
+ if (deptService.hasChildByDeptId(deptId))
+ {
+ return warn("存在下级部门,不允许删除");
+ }
+ if (deptService.checkDeptExistUser(deptId))
+ {
+ return warn("部门存在用户,不允许删除");
+ }
+ deptService.checkDeptDataScope(deptId);
+ return toAjax(deptService.deleteDeptById(deptId));
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysDictDataController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysDictDataController.java
new file mode 100644
index 0000000..e36ccde
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysDictDataController.java
@@ -0,0 +1,121 @@
+package com.bs.web.controller.system;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysDictData;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.StringUtils;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.service.ISysDictDataService;
+import com.bs.system.service.ISysDictTypeService;
+
+/**
+ * 数据字典信息
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/dict/data")
+public class SysDictDataController extends BaseController
+{
+ @Autowired
+ private ISysDictDataService dictDataService;
+
+ @Autowired
+ private ISysDictTypeService dictTypeService;
+
+ @PreAuthorize("@ss.hasPermi('system:dict:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysDictData dictData)
+ {
+ startPage();
+ List list = dictDataService.selectDictDataList(dictData);
+ return getDataTable(list);
+ }
+
+ @Log(title = "字典数据", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:dict:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysDictData dictData)
+ {
+ List list = dictDataService.selectDictDataList(dictData);
+ ExcelUtil util = new ExcelUtil(SysDictData.class);
+ util.exportExcel(response, list, "字典数据");
+ }
+
+ /**
+ * 查询字典数据详细
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:query')")
+ @GetMapping(value = "/{dictCode}")
+ public AjaxResult getInfo(@PathVariable Long dictCode)
+ {
+ return success(dictDataService.selectDictDataById(dictCode));
+ }
+
+ /**
+ * 根据字典类型查询字典数据信息
+ */
+ @GetMapping(value = "/type/{dictType}")
+ public AjaxResult dictType(@PathVariable String dictType)
+ {
+ List data = dictTypeService.selectDictDataByType(dictType);
+ if (StringUtils.isNull(data))
+ {
+ data = new ArrayList();
+ }
+ return success(data);
+ }
+
+ /**
+ * 新增字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:add')")
+ @Log(title = "字典数据", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysDictData dict)
+ {
+ dict.setCreateBy(getUsername());
+ return toAjax(dictDataService.insertDictData(dict));
+ }
+
+ /**
+ * 修改保存字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:edit')")
+ @Log(title = "字典数据", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysDictData dict)
+ {
+ dict.setUpdateBy(getUsername());
+ return toAjax(dictDataService.updateDictData(dict));
+ }
+
+ /**
+ * 删除字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+ @Log(title = "字典类型", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{dictCodes}")
+ public AjaxResult remove(@PathVariable Long[] dictCodes)
+ {
+ dictDataService.deleteDictDataByIds(dictCodes);
+ return success();
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysDictTypeController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysDictTypeController.java
new file mode 100644
index 0000000..12aa620
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysDictTypeController.java
@@ -0,0 +1,131 @@
+package com.bs.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysDictType;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.service.ISysDictTypeService;
+
+/**
+ * 数据字典信息
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/dict/type")
+public class SysDictTypeController extends BaseController
+{
+ @Autowired
+ private ISysDictTypeService dictTypeService;
+
+ @PreAuthorize("@ss.hasPermi('system:dict:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysDictType dictType)
+ {
+ startPage();
+ List list = dictTypeService.selectDictTypeList(dictType);
+ return getDataTable(list);
+ }
+
+ @Log(title = "字典类型", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:dict:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysDictType dictType)
+ {
+ List list = dictTypeService.selectDictTypeList(dictType);
+ ExcelUtil util = new ExcelUtil(SysDictType.class);
+ util.exportExcel(response, list, "字典类型");
+ }
+
+ /**
+ * 查询字典类型详细
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:query')")
+ @GetMapping(value = "/{dictId}")
+ public AjaxResult getInfo(@PathVariable Long dictId)
+ {
+ return success(dictTypeService.selectDictTypeById(dictId));
+ }
+
+ /**
+ * 新增字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:add')")
+ @Log(title = "字典类型", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysDictType dict)
+ {
+ if (!dictTypeService.checkDictTypeUnique(dict))
+ {
+ return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
+ }
+ dict.setCreateBy(getUsername());
+ return toAjax(dictTypeService.insertDictType(dict));
+ }
+
+ /**
+ * 修改字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:edit')")
+ @Log(title = "字典类型", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysDictType dict)
+ {
+ if (!dictTypeService.checkDictTypeUnique(dict))
+ {
+ return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
+ }
+ dict.setUpdateBy(getUsername());
+ return toAjax(dictTypeService.updateDictType(dict));
+ }
+
+ /**
+ * 删除字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+ @Log(title = "字典类型", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{dictIds}")
+ public AjaxResult remove(@PathVariable Long[] dictIds)
+ {
+ dictTypeService.deleteDictTypeByIds(dictIds);
+ return success();
+ }
+
+ /**
+ * 刷新字典缓存
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+ @Log(title = "字典类型", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/refreshCache")
+ public AjaxResult refreshCache()
+ {
+ dictTypeService.resetDictCache();
+ return success();
+ }
+
+ /**
+ * 获取字典选择框列表
+ */
+ @GetMapping("/optionselect")
+ public AjaxResult optionselect()
+ {
+ List dictTypes = dictTypeService.selectDictTypeAll();
+ return success(dictTypes);
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysIndexController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysIndexController.java
new file mode 100644
index 0000000..4c8eb63
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysIndexController.java
@@ -0,0 +1,29 @@
+package com.bs.web.controller.system;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.config.BsConfig;
+import com.bs.common.utils.StringUtils;
+
+/**
+ * 首页
+ *
+ * @author bs
+ */
+@RestController
+public class SysIndexController
+{
+ /** 系统基础配置 */
+ @Autowired
+ private BsConfig bsConfig;
+
+ /**
+ * 访问首页,提示语
+ */
+ @RequestMapping("/")
+ public String index()
+ {
+ return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", bsConfig.getName(), bsConfig.getVersion());
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysLoginController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysLoginController.java
new file mode 100644
index 0000000..e10a8fc
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysLoginController.java
@@ -0,0 +1,98 @@
+package com.bs.web.controller.system;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.common.constant.Constants;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysMenu;
+import com.bs.common.core.domain.entity.SysUser;
+import com.bs.common.core.domain.entity.TenantConfig;
+import com.bs.common.core.domain.model.LoginBody;
+import com.bs.common.utils.SecurityUtils;
+import com.bs.framework.web.service.SysLoginService;
+import com.bs.framework.web.service.SysPermissionService;
+import com.bs.system.service.ISysMenuService;
+import com.bs.system.service.ITenantConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 登录验证
+ *
+ * @author bs
+ */
+@RestController
+public class SysLoginController
+{
+ @Autowired
+ private SysLoginService loginService;
+
+ @Autowired
+ private ISysMenuService menuService;
+
+ @Autowired
+ private SysPermissionService permissionService;
+
+ @Autowired
+ private ITenantConfigService tenantConfigService;
+
+ /**
+ * 登录方法
+ *
+ * @param loginBody 登录信息
+ * @return 结果
+ */
+ @PostMapping("/login")
+ public AjaxResult login(@RequestBody LoginBody loginBody)
+ {
+ AjaxResult ajax = AjaxResult.success();
+ // 生成令牌
+ String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
+ loginBody.getUuid());
+ ajax.put(Constants.TOKEN, token);
+ return ajax;
+ }
+
+ /**
+ * 获取用户信息
+ *
+ * @return 用户信息
+ */
+ @GetMapping("getInfo")
+ public AjaxResult getInfo()
+ {
+ SysUser user = SecurityUtils.getLoginUser().getUser();
+ TenantConfig tenantConfig = tenantConfigService.getOne(new LambdaQueryWrapper().eq(TenantConfig::getId, user.getTenantId()));
+ // 角色集合
+ Set roles = permissionService.getRolePermission(user);
+ // 权限集合
+ Set permissions = permissionService.getMenuPermission(user);
+ if(tenantConfig==null){
+ tenantConfig = new TenantConfig();
+ }
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("user", user);
+ ajax.put("tenantConfig", tenantConfig);
+ ajax.put("roles", roles);
+ ajax.put("permissions", permissions);
+ return ajax;
+ }
+
+ /**
+ * 获取路由信息
+ *
+ * @return 路由信息
+ */
+ @GetMapping("getRouters")
+ public AjaxResult getRouters()
+ {
+ Long userId = SecurityUtils.getUserId();
+ List menus = menuService.selectMenuTreeByUserId(userId);
+ return AjaxResult.success(menuService.buildMenus(menus));
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysMenuController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysMenuController.java
new file mode 100644
index 0000000..54dffc9
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysMenuController.java
@@ -0,0 +1,139 @@
+package com.bs.web.controller.system;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.common.annotation.Log;
+import com.bs.common.constant.UserConstants;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysMenu;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.StringUtils;
+import com.bs.system.mapper.SysMenuMapper;
+import com.bs.system.service.ISysMenuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 菜单信息
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/menu")
+public class SysMenuController extends BaseController
+{
+ @Autowired
+ private ISysMenuService menuService;
+ @Autowired
+ private SysMenuMapper sysMenuMapper;
+
+ /**
+ * 获取菜单列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:list')")
+ @GetMapping("/list")
+ public AjaxResult list(SysMenu menu)
+ {
+ List menus = menuService.selectMenuList(menu, getUserId());
+ return success(menus);
+ }
+
+ /**
+ * 根据菜单编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:query')")
+ @GetMapping(value = "/{menuId}")
+ public AjaxResult getInfo(@PathVariable Long menuId)
+ {
+ return success(menuService.selectMenuById(menuId));
+ }
+
+ /**
+ * 获取菜单下拉树列表
+ */
+ @GetMapping("/treeselect")
+ public AjaxResult treeselect(SysMenu menu)
+ {
+ List menus = menuService.selectMenuList(menu, getUserId());
+ return success(menuService.buildMenuTreeSelect(menus));
+ }
+
+ /**
+ * 加载对应角色菜单列表树
+ */
+ @GetMapping(value = "/roleMenuTreeselect/{roleId}")
+ public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
+ {
+ List menus = menuService.selectMenuList(getUserId());
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
+ ajax.put("menus", menuService.buildMenuTreeSelect(menus));
+ return ajax;
+ }
+
+ /**
+ * 新增菜单
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:add')")
+ @Log(title = "菜单管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysMenu menu)
+ {
+ if (!menuService.checkMenuNameUnique(menu))
+ {
+ return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+ }
+ else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
+ {
+ return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+ }
+ menu.setCreateBy(getUsername());
+ return toAjax(menuService.insertMenu(menu));
+ }
+
+ /**
+ * 修改菜单
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:edit')")
+ @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysMenu menu)
+ {
+ if (!menuService.checkMenuNameUnique(menu))
+ {
+ return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+ }
+ else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
+ {
+ return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+ }
+ else if (menu.getMenuId().equals(menu.getParentId()))
+ {
+ return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
+ }
+ menu.setUpdateBy(getUsername());
+ return toAjax(menuService.updateMenu(menu));
+ }
+
+ /**
+ * 删除菜单
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:remove')")
+ @Log(title = "菜单管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{menuId}")
+ public AjaxResult remove(@PathVariable("menuId") Long menuId)
+ {
+
+ if (menuService.checkMenuExistRole(menuId))
+ {
+ return warn("菜单已分配,不允许删除");
+ }
+ //select count(1) from sys_menu where parent_id = #{menuId}
+ sysMenuMapper.delete(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId));
+ return toAjax(menuService.deleteMenuById(menuId));
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysNoticeController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysNoticeController.java
new file mode 100644
index 0000000..ee8aeb5
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysNoticeController.java
@@ -0,0 +1,170 @@
+package com.bs.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import cn.hutool.core.lang.Validator;
+import com.bs.system.domain.SysNotice;
+import com.bs.system.service.ISysNoticeService;
+import javax.annotation.Resource;
+
+/**
+ * 通知公告Controller
+ *
+ * @author bs
+ * @date 2023-08-06
+ */
+@Api(tags = "通知公告")
+@RestController
+@RequestMapping("/system/notice")
+public class SysNoticeController extends BaseController {
+ @Resource
+ private ISysNoticeService sysNoticeService;
+
+ /**
+ * 分页查询通知公告列表
+ */
+ @ApiOperation("分页查询通知公告列表")
+ @GetMapping("/pageList")
+ public TableDataInfo pageList(SysNotice sysNotice) {
+ startPage();
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper,sysNotice);
+ List list = sysNoticeService.list(queryWrapper);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询通知公告列表
+ */
+ @ApiOperation("查询通知公告列表")
+ @GetMapping("/list")
+ public AjaxResult list(SysNotice sysNotice) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper,sysNotice);
+ List list = sysNoticeService.list(queryWrapper);
+ return success(list);
+ }
+
+ /**
+ * 导出通知公告列表
+ */
+ @ApiOperation("导出通知公告列表")
+ @Log(title = "通知公告导出", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysNotice sysNotice) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper,sysNotice);
+ List list = sysNoticeService.list(queryWrapper);
+ ExcelUtil util = new ExcelUtil(SysNotice. class);
+ util.exportExcel(response, list, "通知公告数据");
+ }
+
+ /**
+ * 获取通知公告详细信息
+ */
+ @ApiOperation("获取通知公告详细信息")
+ @GetMapping(value = "/{noticeId}")
+ public AjaxResult getInfo(@PathVariable("noticeId") Long noticeId) {
+ return success(sysNoticeService.getById(noticeId));
+ }
+
+ /**
+ * 新增通知公告
+ */
+ @ApiOperation("新增通知公告")
+ @Log(title = "通知公告新增", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody SysNotice sysNotice) {
+ return toAjax(sysNoticeService.save(sysNotice));
+ }
+
+ /**
+ * 修改通知公告
+ */
+ @ApiOperation("修改通知公告")
+ @Log(title = "通知公告修改", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody SysNotice sysNotice) {
+ return toAjax(sysNoticeService.updateById(sysNotice));
+ }
+
+ /**
+ * 删除通知公告
+ */
+ @ApiOperation("删除通知公告")
+ @Log(title = "通知公告删除", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{noticeIds}")
+ public AjaxResult remove(@PathVariable List noticeIds) {
+ return toAjax(sysNoticeService.removeBatchByIds(noticeIds));
+ }
+
+ /**
+ * 条件设置
+ */
+ private void condition (LambdaQueryWrapper queryWrapper,SysNotice sysNotice){
+
+
+ //公告标题
+ if(Validator.isNotEmpty(sysNotice.getNoticeTitle())){
+ queryWrapper.eq(SysNotice::getNoticeTitle,sysNotice.getNoticeTitle());
+ }
+
+ //公告类型sys_notice_type字典(1通知 2公告)
+ if(Validator.isNotEmpty(sysNotice.getNoticeType())){
+ queryWrapper.eq(SysNotice::getNoticeType,sysNotice.getNoticeType());
+ }
+
+ //公告内容
+ if(Validator.isNotEmpty(sysNotice.getNoticeContent())){
+ queryWrapper.eq(SysNotice::getNoticeContent,sysNotice.getNoticeContent());
+ }
+
+ //公告状态(0正常 1关闭)
+ if(Validator.isNotEmpty(sysNotice.getStatus())){
+ queryWrapper.eq(SysNotice::getStatus,sysNotice.getStatus());
+ }
+
+ //阅读状态(0未阅读 1已阅读)
+ if(Validator.isNotEmpty(sysNotice.getReadStatus())){
+ queryWrapper.eq(SysNotice::getReadStatus,sysNotice.getReadStatus());
+ }
+
+ //查看人
+ if(sysNotice.getReadUser()!=null){
+ queryWrapper.eq(SysNotice::getReadUser,sysNotice.getReadUser());
+ }
+
+ //删除标志(0代表存在 2代表删除)
+ if(Validator.isNotEmpty(sysNotice.getDelFlag())){
+ queryWrapper.eq(SysNotice::getDelFlag,sysNotice.getDelFlag());
+ }
+
+
+ //创建人
+ if(sysNotice.getCreateBy()!=null){
+ queryWrapper.eq(SysNotice::getCreateBy,sysNotice.getCreateBy());
+ }
+
+ }
+
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysPostController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysPostController.java
new file mode 100644
index 0000000..a22dd77
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysPostController.java
@@ -0,0 +1,129 @@
+package com.bs.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.domain.SysPost;
+import com.bs.system.service.ISysPostService;
+
+/**
+ * 岗位信息操作处理
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/post")
+public class SysPostController extends BaseController
+{
+ @Autowired
+ private ISysPostService postService;
+
+ /**
+ * 获取岗位列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:post:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysPost post)
+ {
+ startPage();
+ List list = postService.selectPostList(post);
+ return getDataTable(list);
+ }
+
+ @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:post:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysPost post)
+ {
+ List list = postService.selectPostList(post);
+ ExcelUtil util = new ExcelUtil(SysPost.class);
+ util.exportExcel(response, list, "岗位数据");
+ }
+
+ /**
+ * 根据岗位编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:post:query')")
+ @GetMapping(value = "/{postId}")
+ public AjaxResult getInfo(@PathVariable Long postId)
+ {
+ return success(postService.selectPostById(postId));
+ }
+
+ /**
+ * 新增岗位
+ */
+ @PreAuthorize("@ss.hasPermi('system:post:add')")
+ @Log(title = "岗位管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysPost post)
+ {
+ if (!postService.checkPostNameUnique(post))
+ {
+ return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
+ }
+ else if (!postService.checkPostCodeUnique(post))
+ {
+ return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
+ }
+ post.setCreateBy(getUsername());
+ return toAjax(postService.insertPost(post));
+ }
+
+ /**
+ * 修改岗位
+ */
+ @PreAuthorize("@ss.hasPermi('system:post:edit')")
+ @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysPost post)
+ {
+ if (!postService.checkPostNameUnique(post))
+ {
+ return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
+ }
+ else if (!postService.checkPostCodeUnique(post))
+ {
+ return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
+ }
+ post.setUpdateBy(getUsername());
+ return toAjax(postService.updatePost(post));
+ }
+
+ /**
+ * 删除岗位
+ */
+ @PreAuthorize("@ss.hasPermi('system:post:remove')")
+ @Log(title = "岗位管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{postIds}")
+ public AjaxResult remove(@PathVariable Long[] postIds)
+ {
+ return toAjax(postService.deletePostByIds(postIds));
+ }
+
+ /**
+ * 获取岗位选择框列表
+ */
+ @GetMapping("/optionselect")
+ public AjaxResult optionselect()
+ {
+ List posts = postService.selectPostAll();
+ return success(posts);
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysProfileController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysProfileController.java
new file mode 100644
index 0000000..a1410d7
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysProfileController.java
@@ -0,0 +1,147 @@
+package com.bs.web.controller.system;
+
+import cn.hutool.core.lang.Validator;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.common.annotation.Log;
+import com.bs.common.config.BsConfig;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysTenant;
+import com.bs.common.core.domain.entity.SysUser;
+import com.bs.common.core.domain.model.LoginUser;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.SecurityUtils;
+import com.bs.common.utils.file.FileUploadUtils;
+import com.bs.common.utils.file.MimeTypeUtils;
+import com.bs.framework.web.service.TokenService;
+import com.bs.system.service.ISysUserService;
+import com.bs.system.service.SysTenantService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 个人信息 业务处理
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/user/profile")
+public class SysProfileController extends BaseController
+{
+ @Autowired
+ private ISysUserService userService;
+ @Autowired
+ private SysTenantService sysTenantService;
+
+ @Autowired
+ private TokenService tokenService;
+
+ /**
+ * 个人信息
+ */
+ @GetMapping
+ public AjaxResult profile()
+ {
+ LoginUser loginUser = getLoginUser();
+ SysUser user = loginUser.getUser();
+ AjaxResult ajax = AjaxResult.success(user);
+ ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
+ ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
+ return ajax;
+ }
+
+ /**
+ * 修改用户
+ */
+ @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult updateProfile(@RequestBody SysUser user)
+ {
+ LoginUser loginUser = getLoginUser();
+ SysUser sysUser = loginUser.getUser();
+ user.setUserName(sysUser.getUserName());
+ if (Validator.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
+ {
+ return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
+ }
+ if (Validator.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
+ {
+ return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+ }
+ user.setUserId(sysUser.getUserId());
+ user.setPassword(null);
+ user.setAvatar(null);
+ user.setDeptId(null);
+ if (userService.updateUserProfile(user) > 0)
+ {
+ // 更新缓存用户信息
+ sysUser.setNickName(user.getNickName());
+ sysUser.setPhonenumber(user.getPhonenumber());
+ sysUser.setEmail(user.getEmail());
+ sysUser.setSex(user.getSex());
+ tokenService.setLoginUser(loginUser);
+ return success();
+ }
+ return error("修改个人信息异常,请联系管理员");
+ }
+
+ /**
+ * 重置密码
+ */
+ @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+ @PutMapping("/updatePwd")
+ public AjaxResult updatePwd(String oldPassword, String newPassword)
+ {
+ LoginUser loginUser = getLoginUser();
+ String userName = loginUser.getUsername();
+ String password = loginUser.getPassword();
+ if (!SecurityUtils.matchesPassword(oldPassword, password))
+ {
+ return error("修改密码失败,旧密码错误");
+ }
+ if (SecurityUtils.matchesPassword(newPassword, password))
+ {
+ return error("新密码不能与旧密码相同");
+ }
+ if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
+ {
+ SysUser user = userService.getOne(new LambdaQueryWrapper().eq(SysUser::getUserName, userName));
+ //如果是租户管理员,密码写一份到租户信息
+ if(user.getIsTenantAdmin().equals("Y")){
+ SysTenant sysTenant = sysTenantService.getById(user.getTenantId());
+ sysTenant.setPassword(newPassword);
+ sysTenantService.updateById(sysTenant);
+ }
+ // 更新缓存用户密码
+ loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
+ tokenService.setLoginUser(loginUser);
+ return success();
+ }
+ return error("修改密码异常,请联系管理员");
+ }
+
+ /**
+ * 头像上传
+ */
+ @Log(title = "用户头像", businessType = BusinessType.UPDATE)
+ @PostMapping("/avatar")
+ public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
+ {
+ if (!file.isEmpty())
+ {
+ LoginUser loginUser = getLoginUser();
+ String avatar = FileUploadUtils.upload(BsConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
+ if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
+ {
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("imgUrl", avatar);
+ // 更新缓存用户头像
+ loginUser.getUser().setAvatar(avatar);
+ tokenService.setLoginUser(loginUser);
+ return ajax;
+ }
+ }
+ return error("上传图片异常,请联系管理员");
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysRegisterController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysRegisterController.java
new file mode 100644
index 0000000..bd524c6
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysRegisterController.java
@@ -0,0 +1,38 @@
+package com.bs.web.controller.system;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.model.RegisterBody;
+import com.bs.common.utils.StringUtils;
+import com.bs.framework.web.service.SysRegisterService;
+import com.bs.system.service.ISysConfigService;
+
+/**
+ * 注册验证
+ *
+ * @author bs
+ */
+@RestController
+public class SysRegisterController extends BaseController
+{
+ @Autowired
+ private SysRegisterService registerService;
+
+ @Autowired
+ private ISysConfigService configService;
+
+ @PostMapping("/register")
+ public AjaxResult register(@RequestBody RegisterBody user)
+ {
+ if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
+ {
+ return error("当前系统没有开启注册功能!");
+ }
+ String msg = registerService.register(user);
+ return StringUtils.isEmpty(msg) ? success() : error(msg);
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysRoleController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysRoleController.java
new file mode 100644
index 0000000..948e240
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysRoleController.java
@@ -0,0 +1,262 @@
+package com.bs.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysDept;
+import com.bs.common.core.domain.entity.SysRole;
+import com.bs.common.core.domain.entity.SysUser;
+import com.bs.common.core.domain.model.LoginUser;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.StringUtils;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.framework.web.service.SysPermissionService;
+import com.bs.framework.web.service.TokenService;
+import com.bs.system.domain.SysUserRole;
+import com.bs.system.service.ISysDeptService;
+import com.bs.system.service.ISysRoleService;
+import com.bs.system.service.ISysUserService;
+
+/**
+ * 角色信息
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/role")
+public class SysRoleController extends BaseController
+{
+ @Autowired
+ private ISysRoleService roleService;
+
+ @Autowired
+ private TokenService tokenService;
+
+ @Autowired
+ private SysPermissionService permissionService;
+
+ @Autowired
+ private ISysUserService userService;
+
+ @Autowired
+ private ISysDeptService deptService;
+
+ @PreAuthorize("@ss.hasPermi('system:role:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysRole role)
+ {
+ startPage();
+ List list = roleService.selectRoleList(role);
+ return getDataTable(list);
+ }
+
+ @Log(title = "角色管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:role:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysRole role)
+ {
+ List list = roleService.selectRoleList(role);
+ ExcelUtil util = new ExcelUtil(SysRole.class);
+ util.exportExcel(response, list, "角色数据");
+ }
+
+ /**
+ * 根据角色编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:query')")
+ @GetMapping(value = "/{roleId}")
+ public AjaxResult getInfo(@PathVariable Long roleId)
+ {
+ roleService.checkRoleDataScope(roleId);
+ return success(roleService.selectRoleById(roleId));
+ }
+
+ /**
+ * 新增角色
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:add')")
+ @Log(title = "角色管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysRole role)
+ {
+ if (!roleService.checkRoleNameUnique(role))
+ {
+ return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
+ }
+ else if (!roleService.checkRoleKeyUnique(role))
+ {
+ return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
+ }
+ role.setCreateBy(getUsername());
+ return toAjax(roleService.insertRole(role));
+
+ }
+
+ /**
+ * 修改保存角色
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:edit')")
+ @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysRole role)
+ {
+ roleService.checkRoleAllowed(role);
+ roleService.checkRoleDataScope(role.getRoleId());
+ if (!roleService.checkRoleNameUnique(role))
+ {
+ return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
+ }
+ else if (!roleService.checkRoleKeyUnique(role))
+ {
+ return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
+ }
+ role.setUpdateBy(getUsername());
+
+ if (roleService.updateRole(role) > 0)
+ {
+ // 更新缓存用户权限
+ LoginUser loginUser = getLoginUser();
+ if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
+ {
+ loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
+ loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
+ tokenService.setLoginUser(loginUser);
+ }
+ return success();
+ }
+ return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
+ }
+
+ /**
+ * 修改保存数据权限
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:edit')")
+ @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+ @PutMapping("/dataScope")
+ public AjaxResult dataScope(@RequestBody SysRole role)
+ {
+ roleService.checkRoleAllowed(role);
+ roleService.checkRoleDataScope(role.getRoleId());
+ return toAjax(roleService.authDataScope(role));
+ }
+
+ /**
+ * 状态修改
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:edit')")
+ @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody SysRole role)
+ {
+ roleService.checkRoleAllowed(role);
+ roleService.checkRoleDataScope(role.getRoleId());
+ role.setUpdateBy(getUsername());
+ return toAjax(roleService.updateRoleStatus(role));
+ }
+
+ /**
+ * 删除角色
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:remove')")
+ @Log(title = "角色管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{roleIds}")
+ public AjaxResult remove(@PathVariable Long[] roleIds)
+ {
+ return toAjax(roleService.deleteRoleByIds(roleIds));
+ }
+
+ /**
+ * 获取角色选择框列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:query')")
+ @GetMapping("/optionselect")
+ public AjaxResult optionselect()
+ {
+ return success(roleService.selectRoleAll());
+ }
+
+ /**
+ * 查询已分配用户角色列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:list')")
+ @GetMapping("/authUser/allocatedList")
+ public TableDataInfo allocatedList(SysUser user)
+ {
+ startPage();
+ List list = userService.selectAllocatedList(user);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询未分配用户角色列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:list')")
+ @GetMapping("/authUser/unallocatedList")
+ public TableDataInfo unallocatedList(SysUser user)
+ {
+ startPage();
+ List list = userService.selectUnallocatedList(user);
+ return getDataTable(list);
+ }
+
+ /**
+ * 取消授权用户
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:edit')")
+ @Log(title = "角色管理", businessType = BusinessType.GRANT)
+ @PutMapping("/authUser/cancel")
+ public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole)
+ {
+ return toAjax(roleService.deleteAuthUser(userRole));
+ }
+
+ /**
+ * 批量取消授权用户
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:edit')")
+ @Log(title = "角色管理", businessType = BusinessType.GRANT)
+ @PutMapping("/authUser/cancelAll")
+ public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds)
+ {
+ return toAjax(roleService.deleteAuthUsers(roleId, userIds));
+ }
+
+ /**
+ * 批量选择用户授权
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:edit')")
+ @Log(title = "角色管理", businessType = BusinessType.GRANT)
+ @PutMapping("/authUser/selectAll")
+ public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
+ {
+ roleService.checkRoleDataScope(roleId);
+ return toAjax(roleService.insertAuthUsers(roleId, userIds));
+ }
+
+ /**
+ * 获取对应角色部门树列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:role:query')")
+ @GetMapping(value = "/deptTree/{roleId}")
+ public AjaxResult deptTree(@PathVariable("roleId") Long roleId)
+ {
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
+ ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
+ return ajax;
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysTenantController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysTenantController.java
new file mode 100644
index 0000000..f607b28
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysTenantController.java
@@ -0,0 +1,335 @@
+package com.bs.web.controller.system;
+
+import cn.hutool.core.lang.Validator;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysDept;
+import com.bs.common.core.domain.entity.SysTenant;
+import com.bs.common.core.domain.entity.SysUser;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.SecurityUtils;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.domain.SysPost;
+import com.bs.system.domain.SysUserRole;
+import com.bs.system.mapper.SysDeptMapper;
+import com.bs.system.mapper.SysPostMapper;
+import com.bs.system.mapper.SysUserMapper;
+import com.bs.system.mapper.SysUserRoleMapper;
+import com.bs.system.service.ISysUserService;
+import com.bs.system.service.SysTenantService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 租户信息Controller
+ *
+ * @author bs
+ * @date 2024-02-22
+ */
+@Api(tags = "租户信息")
+@RestController
+@RequestMapping("/system/tenant")
+public class SysTenantController extends BaseController {
+ @Resource
+ private SysTenantService sysTenantService;
+ @Autowired
+ private ISysUserService userService;
+
+ @Autowired
+ private SysUserMapper userMapper;
+ @Autowired
+ private SysUserRoleMapper sysUserRoleMapper;
+
+ @Autowired
+ private SysDeptMapper deptMapper;
+
+ @Autowired
+ private SysPostMapper postMapepr;
+
+ /**
+ * 分页查询租户信息列表
+ */
+ @ApiOperation("分页查询租户信息列表")
+ @GetMapping("/pageList")
+ public TableDataInfo pageList(SysTenant sysTenant) {
+ startPage();
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, sysTenant);
+ List list = sysTenantService.list(queryWrapper);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询租户信息列表
+ */
+ @ApiOperation("查询租户信息列表")
+ @GetMapping("/list")
+ public AjaxResult list(SysTenant sysTenant) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, sysTenant);
+ List list = sysTenantService.list(queryWrapper);
+ return success(list);
+ }
+
+ /**
+ * 导出租户信息列表
+ */
+ @ApiOperation("导出租户信息列表")
+ @Log(title = "租户信息导出", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysTenant sysTenant) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, sysTenant);
+ List list = sysTenantService.list(queryWrapper);
+ ExcelUtil util = new ExcelUtil(SysTenant.class);
+ util.exportExcel(response, list, "租户信息数据");
+ }
+
+ /**
+ * 获取租户信息详细信息
+ */
+ @ApiOperation("获取租户信息详细信息")
+ @GetMapping(value = "/{id}")
+ public AjaxResult getInfo(@PathVariable("id") Long id) {
+ final SysTenant sysTenant = sysTenantService.getById(id);
+ SysUser sysUser = userMapper.selectTenantAdmin(sysTenant.getId());
+ final List sysUserRoles = sysUserRoleMapper.selectList(new LambdaQueryWrapper().eq(SysUserRole::getUserId, sysUser.getUserId()));
+ final List roleIds = sysUserRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
+ sysTenant.setRoleIds(roleIds);
+ return success(sysTenant);
+ }
+
+ /**
+ * 新增租户信息
+ */
+ @ApiOperation("新增租户信息")
+ @Log(title = "租户信息新增", businessType = BusinessType.INSERT)
+ @PostMapping
+ @Transactional(rollbackFor = Exception.class)
+ public AjaxResult add(@RequestBody SysTenant sysTenant) {
+ //新增租户的时候,进行租户管理员账号的添加
+ sysTenantService.save(sysTenant);
+ SysUser user = new SysUser();
+ user.setNickName(sysTenant.getName() + "管理员");
+ user.setUserName(sysTenant.getUserName());
+ user.setPassword(sysTenant.getPassword());
+ user.setStatus("0");
+ user.setIsTenant("Y");
+ user.setIsTenantAdmin("Y");
+ user.setPhonenumber(sysTenant.getContactPhone());
+ if (!userService.checkUserNameUnique(user)) {
+ return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
+ }
+ if (Validator.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
+ return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
+ }
+ user.setCreateBy(getUsername());
+ user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
+ user.setTenantId(sysTenant.getId());
+
+ initTenantDate(user);
+ userMapper.insert(user);
+ List userRoles = new ArrayList<>();
+ sysTenant.getRoleIds().forEach(roleId -> {
+ SysUserRole sysUserRole = new SysUserRole();
+ sysUserRole.setUserId(user.getUserId());
+ sysUserRole.setRoleId(roleId);
+ userRoles.add(sysUserRole);
+ });
+ sysUserRoleMapper.insertBatch(userRoles);
+ return success("添加成功");
+ }
+
+ /**
+ * 初始化租户的数据
+ * 新建部门,复制一份基础租户的角色,配置给admin
+ * 复制一份字典给租户
+ */
+ public void initTenantDate(SysUser user) {
+ //查询 sys_dept 表,租户id为空的,复制一份
+ //初始化部门
+ SysDept parentsysDepts = new SysDept();
+ parentsysDepts.setParentId(0L);
+ parentsysDepts.setDeptName("总公司");
+ parentsysDepts.setOrderNum(0);
+ parentsysDepts.setAncestors("0");
+ parentsysDepts.setLeader("admin");
+ parentsysDepts.setPhone("15888888888");
+ parentsysDepts.setEmail("zgs@qq.com");
+ parentsysDepts.setTenantId(user.getTenantId());
+ deptMapper.insert(parentsysDepts);
+ SysDept sonSysDepts = new SysDept();
+ sonSysDepts.setParentId(parentsysDepts.getDeptId());
+ sonSysDepts.setDeptName("福建分公司");
+ sonSysDepts.setOrderNum(1);
+ //祖辈id列表
+ sonSysDepts.setAncestors("0," + parentsysDepts.getDeptId());
+ sonSysDepts.setLeader("admin");
+ sonSysDepts.setPhone("15888888888");
+ sonSysDepts.setEmail("zgs@qq.com");
+ sonSysDepts.setTenantId(user.getTenantId());
+ deptMapper.insert(sonSysDepts);
+ //初始化岗位
+ List sysPosts = postMapepr.selectList(new LambdaQueryWrapper().eq(SysPost::getDelFlag, "0").eq(SysPost::getTenantId, "0"));
+ sysPosts.forEach(ele -> {
+ ele.setTenantId(user.getTenantId());
+ ele.setPostId(null);
+ });
+ postMapepr.insertBatch(sysPosts);
+ //最高部门
+ user.setDeptId(parentsysDepts.getDeptId());
+ //ceo
+ Long[] postIds = new Long[1];
+ sysPosts.forEach(ele -> {
+ if (ele.getPostCode().equals("ceo")) {
+ postIds[0] = ele.getPostId();
+ }
+ });
+ user.setPostIds(postIds);
+ }
+
+
+ /**
+ * 修改租户信息
+ */
+ @ApiOperation("修改租户信息")
+ @Log(title = "租户信息修改", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody SysTenant sysTenant) {
+ //修改租户,判断密码是否有更换,如有更换进行修改用户密码
+ SysTenant tenant = sysTenantService.getById(sysTenant.getId());
+ SysUser user = userMapper.selectTenantAdmin(sysTenant.getId());
+ if (!tenant.getPassword().equals(sysTenant.getPassword()) || !tenant.getUserName().equals(sysTenant.getUserName())) {
+
+ user.setPassword(SecurityUtils.encryptPassword(sysTenant.getPassword()));
+ user.setUserName(sysTenant.getUserName());
+ userService.resetPwd(user);
+ }
+ //修改租户的角色信息
+ sysUserRoleMapper.delete(new LambdaUpdateWrapper().eq(SysUserRole::getUserId, user.getUserId()));
+ List userRoles = new ArrayList<>();
+ sysTenant.getRoleIds().forEach(roleId -> {
+ SysUserRole sysUserRole = new SysUserRole();
+ sysUserRole.setUserId(user.getUserId());
+ sysUserRole.setRoleId(roleId);
+ userRoles.add(sysUserRole);
+ });
+ if(userRoles.size()>0){
+ sysUserRoleMapper.insertBatch(userRoles);
+ }
+ return toAjax(sysTenantService.updateById(sysTenant));
+ }
+
+ /**
+ * 删除租户信息
+ */
+ @ApiOperation("删除租户信息")
+ @Log(title = "租户信息删除", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public AjaxResult remove(@PathVariable List ids) {
+ return toAjax(sysTenantService.removeBatchByIds(ids));
+ }
+
+ /**
+ * 条件设置
+ */
+ private void condition(LambdaQueryWrapper queryWrapper, SysTenant sysTenant) {
+
+
+ //用户ID
+ if (Validator.isNotEmpty(sysTenant.getId())) {
+ queryWrapper.eq(SysTenant::getId, sysTenant.getId());
+ }
+
+ //租户名
+ if (Validator.isNotEmpty(sysTenant.getName())) {
+ queryWrapper.eq(SysTenant::getName, sysTenant.getName());
+ }
+
+ //租户简名
+ if (Validator.isNotEmpty(sysTenant.getSimpleName())) {
+ queryWrapper.eq(SysTenant::getSimpleName, sysTenant.getSimpleName());
+ }
+
+ //行政区划
+ if (Validator.isNotEmpty(sysTenant.getAreaCode())) {
+ queryWrapper.eq(SysTenant::getAreaCode, sysTenant.getAreaCode());
+ }
+
+
+ //租户密码
+ if (Validator.isNotEmpty(sysTenant.getPassword())) {
+ queryWrapper.eq(SysTenant::getPassword, sysTenant.getPassword());
+ }
+
+ //租户账号
+ if (Validator.isNotEmpty(sysTenant.getUserName())) {
+ queryWrapper.eq(SysTenant::getUserName, sysTenant.getUserName());
+ }
+
+ //联系人
+ if (Validator.isNotEmpty(sysTenant.getContactUser())) {
+ queryWrapper.eq(SysTenant::getContactUser, sysTenant.getContactUser());
+ }
+
+ //联系电话
+ if (Validator.isNotEmpty(sysTenant.getContactPhone())) {
+ queryWrapper.eq(SysTenant::getContactPhone, sysTenant.getContactPhone());
+ }
+
+ //状态,启用1,禁用0,即将过期2
+ if (Validator.isNotEmpty(sysTenant.getStatus())) {
+ queryWrapper.eq(SysTenant::getStatus, sysTenant.getStatus());
+ }
+
+ //创建部门
+ if (Validator.isNotEmpty(sysTenant.getCreateDept())) {
+ queryWrapper.eq(SysTenant::getCreateDept, sysTenant.getCreateDept());
+ }
+
+ //创建人
+ if (Validator.isNotEmpty(sysTenant.getCreateBy())) {
+ queryWrapper.eq(SysTenant::getCreateBy, sysTenant.getCreateBy());
+ }
+
+ //创建时间
+ if (Validator.isNotEmpty(sysTenant.getCreateTime())) {
+ queryWrapper.eq(SysTenant::getCreateTime, sysTenant.getCreateTime());
+ }
+
+ //修改人
+ if (Validator.isNotEmpty(sysTenant.getUpdateBy())) {
+ queryWrapper.eq(SysTenant::getUpdateBy, sysTenant.getUpdateBy());
+ }
+
+ //修改时间
+ if (Validator.isNotEmpty(sysTenant.getUpdateTime())) {
+ queryWrapper.eq(SysTenant::getUpdateTime, sysTenant.getUpdateTime());
+ }
+
+ //备注
+ if (Validator.isNotEmpty(sysTenant.getRemark())) {
+ queryWrapper.eq(SysTenant::getRemark, sysTenant.getRemark());
+ }
+
+ //删除标志(0代表存在 2代表删除)
+ if (Validator.isNotEmpty(sysTenant.getDelFlag())) {
+ queryWrapper.eq(SysTenant::getDelFlag, sysTenant.getDelFlag());
+ }
+
+ }
+
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/SysUserController.java b/bs-admin/src/main/java/com/bs/web/controller/system/SysUserController.java
new file mode 100644
index 0000000..c9a00ec
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/SysUserController.java
@@ -0,0 +1,239 @@
+package com.bs.web.controller.system;
+
+import cn.hutool.core.lang.Validator;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.common.annotation.Log;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.SysDept;
+import com.bs.common.core.domain.entity.SysRole;
+import com.bs.common.core.domain.entity.SysUser;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.SecurityUtils;
+import com.bs.common.utils.StringUtils;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.domain.SysUserRole;
+import com.bs.system.mapper.SysUserRoleMapper;
+import com.bs.system.service.ISysDeptService;
+import com.bs.system.service.ISysPostService;
+import com.bs.system.service.ISysRoleService;
+import com.bs.system.service.ISysUserService;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 用户信息
+ *
+ * @author bs
+ */
+@RestController
+@RequestMapping("/system/user")
+public class SysUserController extends BaseController {
+ @Autowired
+ private ISysUserService userService;
+
+ @Autowired
+ private ISysRoleService roleService;
+
+ @Autowired
+ private SysUserRoleMapper sysUserRoleMapper;
+
+ @Autowired
+ private ISysDeptService deptService;
+
+ @Autowired
+ private ISysPostService postService;
+
+ /**
+ * 获取用户列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysUser user) {
+ startPage();
+ List list = userService.selectUserList(user);
+ return getDataTable(list);
+ }
+
+ @Log(title = "用户管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:user:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysUser user) {
+ List list = userService.selectUserList(user);
+ ExcelUtil util = new ExcelUtil(SysUser.class);
+ util.exportExcel(response, list, "用户数据");
+ }
+
+ @Log(title = "用户管理", businessType = BusinessType.IMPORT)
+ @PreAuthorize("@ss.hasPermi('system:user:import')")
+ @PostMapping("/importData")
+ public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
+ ExcelUtil util = new ExcelUtil(SysUser.class);
+ List userList = util.importExcel(file.getInputStream());
+ Long operName = getUsername();
+ String message = userService.importUser(userList, updateSupport, operName);
+ return success(message);
+ }
+
+ @PostMapping("/importTemplate")
+ public void importTemplate(HttpServletResponse response) {
+ ExcelUtil util = new ExcelUtil(SysUser.class);
+ util.importTemplateExcel(response, "用户数据");
+ }
+
+ /**
+ * 根据用户编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:query')")
+ @GetMapping(value = {"/", "/{userId}"})
+ public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
+ userService.checkUserDataScope(userId);
+ AjaxResult ajax = AjaxResult.success();
+ List roles = roleService.selectRoleAll();
+ ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
+ ajax.put("posts", postService.selectPostAll());
+ if (StringUtils.isNotNull(userId)) {
+ SysUser sysUser = userService.selectUserById(userId);
+ final List sysUserRoles = sysUserRoleMapper.selectList(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId));
+ ajax.put(AjaxResult.DATA_TAG, sysUser);
+ ajax.put("postIds", postService.selectPostListByUserId(userId));
+ ajax.put("roleIds", sysUserRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList()));
+ }
+ return ajax;
+ }
+
+
+ /**
+ 获取所有非admin角色,用于租户配置
+ */
+ @GetMapping("/getRoles")
+ public AjaxResult getRoles() {
+ List roles = roleService.selectRoleAll();
+ return AjaxResult.success().put("roles", roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
+ }
+
+ /**
+ * 新增用户
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:add')")
+ @Log(title = "用户管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody SysUser user) {
+ if (!userService.checkUserNameUnique(user)) {
+ return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
+ } else if (Validator.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
+ return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
+ } else if (Validator.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
+ return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+ }
+ user.setIsTenant(SecurityUtils.getLoginUser().getIsTenant());
+ user.setIsTenantAdmin("N");
+ user.setCreateBy(getUsername());
+ user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
+ return toAjax(userService.insertUser(user));
+ }
+
+ /**
+ * 修改用户
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:edit')")
+ @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@Validated @RequestBody SysUser user) {
+ userService.checkUserAllowed(user);
+ userService.checkUserDataScope(user.getUserId());
+ if (!userService.checkUserNameUnique(user)) {
+ return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
+ } else if (Validator.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
+ return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
+ } else if (Validator.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
+ return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+ }
+ user.setUpdateBy(getUsername());
+ return toAjax(userService.updateUser(user));
+ }
+
+ /**
+ * 删除用户
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:remove')")
+ @Log(title = "用户管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{userIds}")
+ public AjaxResult remove(@PathVariable Long[] userIds) {
+ if (ArrayUtils.contains(userIds, getUserId())) {
+ return error("当前用户不能删除");
+ }
+ return toAjax(userService.deleteUserByIds(userIds));
+ }
+
+ /**
+ * 重置密码
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
+ @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+ @PutMapping("/resetPwd")
+ public AjaxResult resetPwd(@RequestBody SysUser user) {
+ userService.checkUserAllowed(user);
+ userService.checkUserDataScope(user.getUserId());
+ user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
+ user.setUpdateBy(getUsername());
+ return toAjax(userService.resetPwd(user));
+ }
+
+ /**
+ * 状态修改
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:edit')")
+ @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody SysUser user) {
+ userService.checkUserAllowed(user);
+ userService.checkUserDataScope(user.getUserId());
+ user.setUpdateBy(getUsername());
+ return toAjax(userService.updateUserStatus(user));
+ }
+
+ /**
+ * 根据用户编号获取授权角色
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:query')")
+ @GetMapping("/authRole/{userId}")
+ public AjaxResult authRole(@PathVariable("userId") Long userId) {
+ AjaxResult ajax = AjaxResult.success();
+ SysUser user = userService.selectUserById(userId);
+ List roles = roleService.selectRolesByUserId(userId);
+ ajax.put("user", user);
+ ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
+ return ajax;
+ }
+
+ /**
+ * 用户授权角色
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:edit')")
+ @Log(title = "用户管理", businessType = BusinessType.GRANT)
+ @PutMapping("/authRole")
+ public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
+ userService.checkUserDataScope(userId);
+ userService.insertUserAuth(userId, roleIds);
+ return success();
+ }
+
+ /**
+ * 获取部门树列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:list')")
+ @GetMapping("/deptTree")
+ public AjaxResult deptTree(SysDept dept) {
+ return success(deptService.selectDeptTreeList(dept));
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/system/TenantConfigController.java b/bs-admin/src/main/java/com/bs/web/controller/system/TenantConfigController.java
new file mode 100644
index 0000000..aa4b542
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/system/TenantConfigController.java
@@ -0,0 +1,231 @@
+package com.bs.web.controller.system;
+
+import cn.hutool.core.lang.Validator;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bs.common.annotation.Log;
+import com.bs.common.config.BsConfig;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.entity.TenantConfig;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.utils.file.FileUploadUtils;
+import com.bs.common.utils.file.MimeTypeUtils;
+import com.bs.common.utils.poi.ExcelUtil;
+import com.bs.system.service.ITenantConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 租户配置Controller
+ *
+ * @author ruoyi
+ * @date 2023-04-02
+ */
+@RestController
+@RequestMapping("/tenant/config")
+public class TenantConfigController extends BaseController {
+ @Autowired
+ private ITenantConfigService tenantConfigService;
+
+ /**
+ * 分页查询租户配置列表
+ */
+ @PreAuthorize("@ss.hasPermi('tenant:config:pageList')")
+ @GetMapping("/pageList")
+ public TableDataInfo pageList(TenantConfig tenantConfig) {
+ startPage();
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, tenantConfig);
+ List list = tenantConfigService.list(queryWrapper);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询租户配置列表
+ */
+ @PreAuthorize("@ss.hasPermi('tenant:config:list')")
+ @GetMapping("/list")
+ public AjaxResult list(TenantConfig tenantConfig) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, tenantConfig);
+ List list = tenantConfigService.list(queryWrapper);
+ return success(list);
+ }
+
+ /**
+ * 导出租户配置列表
+ */
+ @Log(title = "租户配置导出", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, TenantConfig tenantConfig) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
+ condition(queryWrapper, tenantConfig);
+ List list = tenantConfigService.list(queryWrapper);
+ ExcelUtil util = new ExcelUtil(TenantConfig.class);
+ util.exportExcel(response, list, "租户配置数据");
+ }
+
+ /**
+ * 获取租户配置详细信息
+ */
+ @GetMapping(value = "/{id}")
+ public AjaxResult getInfo(@PathVariable("id") String id) {
+ TenantConfig tenantConfig = tenantConfigService.getById(id);
+ tenantConfig.setTenantId(tenantConfig.getId()) ;
+ return success(tenantConfig);
+ }
+
+ /**
+ * 新增租户配置
+ */
+ @Log(title = "租户配置新增", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody TenantConfig tenantConfig) {
+ tenantConfig.setId(tenantConfig.getTenantId()) ;
+ return toAjax(tenantConfigService.save(tenantConfig));
+ }
+
+ /**
+ * 修改租户配置
+ */
+ @Log(title = "租户配置修改", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody TenantConfig tenantConfig) {
+ tenantConfig.setId(tenantConfig.getTenantId()) ;
+ return toAjax(tenantConfigService.updateById(tenantConfig));
+ }
+
+ /**
+ * 删除租户配置
+ */
+ @Log(title = "租户配置删除", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public AjaxResult remove(@PathVariable List ids) {
+ return toAjax(tenantConfigService.removeBatchByIds(ids));
+ }
+
+ /**
+ * 租户logo图片上传
+ */
+ @Log(title = "租户logo图片上传", businessType = BusinessType.UPDATE)
+ @PostMapping("/logo")
+ public AjaxResult avatar(@RequestParam("tenantLogo") MultipartFile file) throws Exception {
+ if (!file.isEmpty()) {
+ String logo = FileUploadUtils.upload(BsConfig.getUploadPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
+ AjaxResult ajax = success();
+ ajax.put("imgUrl", logo);
+ return ajax;
+ }
+ return error("上传图片异常,请联系管理员");
+ }
+
+
+
+ /**
+ * 条件设置
+ */
+ private void condition(LambdaQueryWrapper queryWrapper, TenantConfig tenantConfig) {
+
+
+ //${column.columnComment}
+ if (Validator.isNotEmpty(tenantConfig.getId())) {
+ queryWrapper.eq(TenantConfig::getId, tenantConfig.getId());
+ }
+
+ //开始时间
+ if (tenantConfig.getStartData() != null) {
+ queryWrapper.eq(TenantConfig::getStartData, tenantConfig.getStartData());
+ }
+
+ //结束时间
+ if (tenantConfig.getStopData() != null) {
+ queryWrapper.eq(TenantConfig::getStopData, tenantConfig.getStopData());
+ }
+
+ //是否限制用户数
+ if (Validator.isNotEmpty(tenantConfig.getIsUserAstrict())) {
+ queryWrapper.eq(TenantConfig::getIsUserAstrict, tenantConfig.getIsUserAstrict());
+ }
+
+ //用户数
+ if (tenantConfig.getUserQuantity() != null) {
+ queryWrapper.eq(TenantConfig::getUserQuantity, tenantConfig.getUserQuantity());
+ }
+
+ //是否限制在线数
+ if (Validator.isNotEmpty(tenantConfig.getIsOnLine())) {
+ queryWrapper.eq(TenantConfig::getIsOnLine, tenantConfig.getIsOnLine());
+ }
+
+ //在线数
+ if (tenantConfig.getOnLineUserQuantity() != null) {
+ queryWrapper.eq(TenantConfig::getOnLineUserQuantity, tenantConfig.getOnLineUserQuantity());
+ }
+
+ //是否定义系统名称
+ if (Validator.isNotEmpty(tenantConfig.getIsDefinitionName())) {
+ queryWrapper.eq(TenantConfig::getIsDefinitionName, tenantConfig.getIsDefinitionName());
+ }
+
+ //系统名称
+ if (Validator.isNotEmpty(tenantConfig.getSysName())) {
+ queryWrapper.like(TenantConfig::getSysName, tenantConfig.getSysName());
+ }
+
+ //是否定义系统logo
+ if (Validator.isNotEmpty(tenantConfig.getIsDefinitionLogo())) {
+ queryWrapper.eq(TenantConfig::getIsDefinitionLogo, tenantConfig.getIsDefinitionLogo());
+ }
+
+ //系统log
+ if (Validator.isNotEmpty(tenantConfig.getSysLog())) {
+ queryWrapper.eq(TenantConfig::getSysLog, tenantConfig.getSysLog());
+ }
+
+ //是否定义系统色调
+ if (Validator.isNotEmpty(tenantConfig.getIsDefinitionHue())) {
+ queryWrapper.eq(TenantConfig::getIsDefinitionHue, tenantConfig.getIsDefinitionHue());
+ }
+
+ //系统色调
+ if (Validator.isNotEmpty(tenantConfig.getSysHue())) {
+ queryWrapper.eq(TenantConfig::getSysHue, tenantConfig.getSysHue());
+ }
+
+ //定制化数据,键值对{key:{type1:"",type2:"",value:""},key2:{type1:"",type2:"",value:""}}具体操作时候,做具体说明
+ if (Validator.isNotEmpty(tenantConfig.getCustomizationFields())) {
+ queryWrapper.eq(TenantConfig::getCustomizationFields, tenantConfig.getCustomizationFields());
+ }
+
+
+ //创建人员
+ if (Validator.isNotEmpty(tenantConfig.getCreateBy())) {
+ queryWrapper.eq(TenantConfig::getCreateBy, tenantConfig.getCreateBy());
+ }
+
+
+ //修改人员
+ if (Validator.isNotEmpty(tenantConfig.getUpdateBy())) {
+ queryWrapper.eq(TenantConfig::getUpdateBy, tenantConfig.getUpdateBy());
+ }
+
+
+ //部门ID
+ if (Validator.isNotEmpty(tenantConfig.getCreateDept())) {
+ queryWrapper.eq(TenantConfig::getCreateDept, tenantConfig.getCreateDept());
+ }
+
+ //删除标示 默认为1 有效数据 ,0为无效已删除
+ if (Validator.isNotEmpty(tenantConfig.getDelFlag())) {
+ queryWrapper.eq(TenantConfig::getDelFlag, tenantConfig.getDelFlag());
+ }
+
+ }
+
+}
diff --git a/bs-admin/src/main/java/com/bs/web/controller/tool/TestController.java b/bs-admin/src/main/java/com/bs/web/controller/tool/TestController.java
new file mode 100644
index 0000000..e2ef0df
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/controller/tool/TestController.java
@@ -0,0 +1,183 @@
+package com.bs.web.controller.tool;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.bs.common.core.controller.BaseController;
+import com.bs.common.core.domain.R;
+import com.bs.common.utils.StringUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * swagger 用户测试方法
+ *
+ * @author bs
+ */
+@Api("用户信息管理")
+@RestController
+@RequestMapping("/test/user")
+public class TestController extends BaseController
+{
+ private final static Map users = new LinkedHashMap();
+ {
+ users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
+ users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
+ }
+
+ @ApiOperation("获取用户列表")
+ @GetMapping("/list")
+ public R> userList()
+ {
+ List userList = new ArrayList(users.values());
+ return R.ok(userList);
+ }
+
+ @ApiOperation("获取用户详细")
+ @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
+ @GetMapping("/{userId}")
+ public R getUser(@PathVariable Integer userId)
+ {
+ if (!users.isEmpty() && users.containsKey(userId))
+ {
+ return R.ok(users.get(userId));
+ }
+ else
+ {
+ return R.fail("用户不存在");
+ }
+ }
+
+ @ApiOperation("新增用户")
+ @ApiImplicitParams({
+ @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
+ @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
+ @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
+ @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
+ })
+ @PostMapping("/save")
+ public R save(UserEntity user)
+ {
+ if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
+ {
+ return R.fail("用户ID不能为空");
+ }
+ users.put(user.getUserId(), user);
+ return R.ok();
+ }
+
+ @ApiOperation("更新用户")
+ @PutMapping("/update")
+ public R update(@RequestBody UserEntity user)
+ {
+ if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
+ {
+ return R.fail("用户ID不能为空");
+ }
+ if (users.isEmpty() || !users.containsKey(user.getUserId()))
+ {
+ return R.fail("用户不存在");
+ }
+ users.remove(user.getUserId());
+ users.put(user.getUserId(), user);
+ return R.ok();
+ }
+
+ @ApiOperation("删除用户信息")
+ @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
+ @DeleteMapping("/{userId}")
+ public R delete(@PathVariable Integer userId)
+ {
+ if (!users.isEmpty() && users.containsKey(userId))
+ {
+ users.remove(userId);
+ return R.ok();
+ }
+ else
+ {
+ return R.fail("用户不存在");
+ }
+ }
+}
+
+@ApiModel(value = "UserEntity", description = "用户实体")
+class UserEntity
+{
+ @ApiModelProperty("用户ID")
+ private Integer userId;
+
+ @ApiModelProperty("用户名称")
+ private String username;
+
+ @ApiModelProperty("用户密码")
+ private String password;
+
+ @ApiModelProperty("用户手机")
+ private String mobile;
+
+ public UserEntity()
+ {
+
+ }
+
+ public UserEntity(Integer userId, String username, String password, String mobile)
+ {
+ this.userId = userId;
+ this.username = username;
+ this.password = password;
+ this.mobile = mobile;
+ }
+
+ public Integer getUserId()
+ {
+ return userId;
+ }
+
+ public void setUserId(Integer userId)
+ {
+ this.userId = userId;
+ }
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public void setUsername(String username)
+ {
+ this.username = username;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public String getMobile()
+ {
+ return mobile;
+ }
+
+ public void setMobile(String mobile)
+ {
+ this.mobile = mobile;
+ }
+}
diff --git a/bs-admin/src/main/java/com/bs/web/core/config/SwaggerConfig.java b/bs-admin/src/main/java/com/bs/web/core/config/SwaggerConfig.java
new file mode 100644
index 0000000..13367a1
--- /dev/null
+++ b/bs-admin/src/main/java/com/bs/web/core/config/SwaggerConfig.java
@@ -0,0 +1,125 @@
+package com.bs.web.core.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.bs.common.config.BsConfig;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.models.auth.In;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+
+/**
+ * Swagger2的接口配置
+ *
+ * @author bs
+ */
+@Configuration
+public class SwaggerConfig
+{
+ /** 系统基础配置 */
+ @Autowired
+ private BsConfig bsConfig;
+
+ /** 是否开启swagger */
+ @Value("${swagger.enabled}")
+ private boolean enabled;
+
+ /** 设置请求的统一前缀 */
+ @Value("${swagger.pathMapping}")
+ private String pathMapping;
+
+ /**
+ * 创建API
+ */
+ @Bean
+ public Docket createRestApi()
+ {
+ return new Docket(DocumentationType.OAS_30)
+ // 是否启用Swagger
+ .enable(enabled)
+ // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
+ .apiInfo(apiInfo())
+ // 设置哪些接口暴露给Swagger展示
+ .select()
+ // 扫描所有有注解的api,用这种方式更灵活
+ .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+ // 扫描指定包中的swagger注解
+ // .apis(RequestHandlerSelectors.basePackage("com.bs.project.tool.swagger"))
+ // 扫描所有 .apis(RequestHandlerSelectors.any())
+ .paths(PathSelectors.any())
+ .build()
+ /* 设置安全模式,swagger可以设置访问token */
+ .securitySchemes(securitySchemes())
+ .securityContexts(securityContexts())
+ .pathMapping(pathMapping);
+ }
+
+ /**
+ * 安全模式,这里指定token通过Authorization头请求头传递
+ */
+ private List securitySchemes()
+ {
+ List apiKeyList = new ArrayList();
+ apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
+ return apiKeyList;
+ }
+
+ /**
+ * 安全上下文
+ */
+ private List securityContexts()
+ {
+ List securityContexts = new ArrayList<>();
+ securityContexts.add(
+ SecurityContext.builder()
+ .securityReferences(defaultAuth())
+ .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
+ .build());
+ return securityContexts;
+ }
+
+ /**
+ * 默认的安全上引用
+ */
+ private List defaultAuth()
+ {
+ AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+ AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+ authorizationScopes[0] = authorizationScope;
+ List securityReferences = new ArrayList<>();
+ securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
+ return securityReferences;
+ }
+
+ /**
+ * 添加摘要信息
+ */
+ private ApiInfo apiInfo()
+ {
+ // 用ApiInfoBuilder进行定制
+ return new ApiInfoBuilder()
+ // 设置标题
+ .title("标题:保税仓库管理系统_接口文档")
+ // 描述
+ .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
+ // 作者信息
+ .contact(new Contact(bsConfig.getName(), null, null))
+ // 版本
+ .version("版本号:" + bsConfig.getVersion())
+ .build();
+ }
+}
diff --git a/bs-admin/src/main/resources/META-INF/spring-devtools.properties b/bs-admin/src/main/resources/META-INF/spring-devtools.properties
new file mode 100644
index 0000000..2b23f85
--- /dev/null
+++ b/bs-admin/src/main/resources/META-INF/spring-devtools.properties
@@ -0,0 +1 @@
+restart.include.json=/com.alibaba.fastjson.*.jar
\ No newline at end of file
diff --git a/bs-admin/src/main/resources/application-druid.yml b/bs-admin/src/main/resources/application-druid.yml
new file mode 100644
index 0000000..a2fe2cb
--- /dev/null
+++ b/bs-admin/src/main/resources/application-druid.yml
@@ -0,0 +1,68 @@
+# 数据源配置
+spring:
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ druid:
+ # 主库数据源
+ master:
+ #url: jdbc:mysql://124.71.134.146:63306/bs-jxc-dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+ #username: root
+ #password: fastrise@chain
+ url: jdbc:mysql://localhost:3306/my-base?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+ username: root
+ password: abcd123
+ # 从库数据源
+ slave:
+ # 从数据源开关/默认关闭
+ enabled: false
+ url:
+ username:
+ password:
+ # 初始连接数
+ initialSize: 5
+ # 最小连接池数量
+ minIdle: 10
+ # 最大连接池数量
+ maxActive: 20
+ # 配置获取连接等待超时的时间
+ maxWait: 60000
+ # 配置连接超时时间
+ connectTimeout: 30000
+ # 配置网络超时时间
+ socketTimeout: 60000
+ # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+ timeBetweenEvictionRunsMillis: 60000
+ # 配置一个连接在池中最小生存的时间,单位是毫秒
+ minEvictableIdleTimeMillis: 300000
+ # 配置一个连接在池中最大生存的时间,单位是毫秒
+ maxEvictableIdleTimeMillis: 900000
+ # 配置检测连接是否有效
+ validationQuery: SELECT 1 FROM DUAL
+ testWhileIdle: true
+ testOnBorrow: false
+ testOnReturn: false
+ webStatFilter:
+ enabled: true
+ statViewServlet:
+ enabled: true
+ # 设置白名单,不填则允许所有访问
+ allow:
+ url-pattern: /druid/*
+ # 控制台管理用户名和密码
+ login-username: bs
+ login-password: 123456
+ filter:
+ stat:
+ enabled: true
+ # 慢SQL记录
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+bs:
+ tenant:
+ #开启租户
+ openTenant: true
\ No newline at end of file
diff --git a/bs-admin/src/main/resources/application-priv.yml b/bs-admin/src/main/resources/application-priv.yml
new file mode 100644
index 0000000..f280d56
--- /dev/null
+++ b/bs-admin/src/main/resources/application-priv.yml
@@ -0,0 +1,68 @@
+# 数据源配置
+spring:
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ druid:
+ # 主库数据源
+ master:
+ #url: jdbc:mysql://124.71.134.146:63306/bs-jxc-dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+ #username: root
+ #password: fastrise@chain
+ url: jdbc:mysql://localhost:3306/bs-jxc-dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+ username: root
+ password: abcd123
+ # 从库数据源
+ slave:
+ # 从数据源开关/默认关闭
+ enabled: false
+ url:
+ username:
+ password:
+ # 初始连接数
+ initialSize: 5
+ # 最小连接池数量
+ minIdle: 10
+ # 最大连接池数量
+ maxActive: 20
+ # 配置获取连接等待超时的时间
+ maxWait: 60000
+ # 配置连接超时时间
+ connectTimeout: 30000
+ # 配置网络超时时间
+ socketTimeout: 60000
+ # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+ timeBetweenEvictionRunsMillis: 60000
+ # 配置一个连接在池中最小生存的时间,单位是毫秒
+ minEvictableIdleTimeMillis: 300000
+ # 配置一个连接在池中最大生存的时间,单位是毫秒
+ maxEvictableIdleTimeMillis: 900000
+ # 配置检测连接是否有效
+ validationQuery: SELECT 1 FROM DUAL
+ testWhileIdle: true
+ testOnBorrow: false
+ testOnReturn: false
+ webStatFilter:
+ enabled: true
+ statViewServlet:
+ enabled: true
+ # 设置白名单,不填则允许所有访问
+ allow:
+ url-pattern: /druid/*
+ # 控制台管理用户名和密码
+ login-username: bs
+ login-password: 123456
+ filter:
+ stat:
+ enabled: true
+ # 慢SQL记录
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+bs:
+ tenant:
+ #关闭租户
+ openTenant: false
\ No newline at end of file
diff --git a/bs-admin/src/main/resources/application-prot.yml b/bs-admin/src/main/resources/application-prot.yml
new file mode 100644
index 0000000..76eb85f
--- /dev/null
+++ b/bs-admin/src/main/resources/application-prot.yml
@@ -0,0 +1,66 @@
+# 数据源配置
+spring:
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ druid:
+ # 主库数据源
+ master:
+ url: jdbc:mysql://124.71.134.146:63306/bs-jxc-prot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+ username: root
+ password: fastrise@chain
+
+ # 从库数据源
+ slave:
+ # 从数据源开关/默认关闭
+ enabled: false
+ url:
+ username:
+ password:
+ # 初始连接数
+ initialSize: 5
+ # 最小连接池数量
+ minIdle: 10
+ # 最大连接池数量
+ maxActive: 20
+ # 配置获取连接等待超时的时间
+ maxWait: 60000
+ # 配置连接超时时间
+ connectTimeout: 30000
+ # 配置网络超时时间
+ socketTimeout: 60000
+ # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+ timeBetweenEvictionRunsMillis: 60000
+ # 配置一个连接在池中最小生存的时间,单位是毫秒
+ minEvictableIdleTimeMillis: 300000
+ # 配置一个连接在池中最大生存的时间,单位是毫秒
+ maxEvictableIdleTimeMillis: 900000
+ # 配置检测连接是否有效
+ validationQuery: SELECT 1 FROM DUAL
+ testWhileIdle: true
+ testOnBorrow: false
+ testOnReturn: false
+ webStatFilter:
+ enabled: true
+ statViewServlet:
+ enabled: true
+ # 设置白名单,不填则允许所有访问
+ allow:
+ url-pattern: /druid/*
+ # 控制台管理用户名和密码
+ login-username: bs
+ login-password: 123456
+ filter:
+ stat:
+ enabled: true
+ # 慢SQL记录
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+bs:
+ tenant:
+ #开启租户
+ openTenant: true
\ No newline at end of file
diff --git a/bs-admin/src/main/resources/application.yml b/bs-admin/src/main/resources/application.yml
new file mode 100644
index 0000000..91ef24a
--- /dev/null
+++ b/bs-admin/src/main/resources/application.yml
@@ -0,0 +1,177 @@
+# Spring配置
+spring:
+ # 资源信息
+ messages:
+ # 国际化资源文件路径
+ basename: i18n/messages
+ profiles:
+ active: druid
+# 项目相关配置
+bs:
+ # 名称
+ name: bs
+ # 版本
+ version: 3.8.6
+ # 版权年份
+ copyrightYear: 2023
+ # 实例演示开关
+ demoEnabled: true
+ # 文件路径 示例( Windows配置D:/bs/uploadPath,Linux配置 /home/bs/uploadPath)
+ profile: D:/bs/uploadPath
+ # 文件存储路径 windows 路径
+ winLocalFilePath: D:/bs/uploadPath
+ # winLocalFilePath: E:/test
+ # 文件存储路径 linux 路径
+ linuxLocalFilePath: /home/chain-mos/attachment
+ # 文件存储路径 mac 路径
+ macLocalFilePath: ~/chain-mos/attachment
+ # 获取ip地址开关
+ addressEnabled: false
+ # 验证码类型 math 数字计算 char 字符验证
+ captchaType: char
+ tenant:
+ column: tenant_id
+ noIgnoreTables:
+ - sys_tenant
+ - tenant_config
+ - gen_table
+ - gen_table_column
+ - sys_user_role
+ - sys_dict_data
+ - sys_dict_type
+ - sys_job
+ - sys_job_log
+ - sys_logininfor
+ - sys_menu
+ - sys_oper_log
+ - sys_role_dept
+ - sys_role_menu
+ - sys_user_post
+ - sys_user_role
+ - sys_config
+# 开发环境配置
+server:
+ # 服务器的HTTP端口,默认为8080
+ port: 8080
+ servlet:
+ # 应用的访问路径
+ context-path: /
+ tomcat:
+ # tomcat的URI编码
+ uri-encoding: UTF-8
+ # 连接数满后的排队数,默认为100
+ accept-count: 1000
+ threads:
+ # tomcat最大线程数,默认为200
+ max: 800
+ # Tomcat启动初始化的线程数,默认值10
+ min-spare: 100
+
+# 日志配置
+logging:
+ level:
+ com.bs: debug
+ org.springframework: warn
+
+# 用户配置
+user:
+ password:
+ # 密码最大错误次数
+ maxRetryCount: 5
+ # 密码锁定时间(默认10分钟)
+ lockTime: 10
+
+
+ # 文件上传
+ servlet:
+ multipart:
+ # 单个文件大小
+ max-file-size: 10MB
+ # 设置总上传的文件大小
+ max-request-size: 20MB
+ # 服务模块
+ devtools:
+ restart:
+ # 热部署开关
+ enabled: true
+ # redis 配置
+ redis:
+ # 地址
+ host: localhost
+ # 端口,默认为6379
+ port: 6379
+ # 数据库索引
+ database: 2
+ # 密码
+ password:
+ # 连接超时时间
+ timeout: 10s
+ lettuce:
+ pool:
+ # 连接池中的最小空闲连接
+ min-idle: 0
+ # 连接池中的最大空闲连接
+ max-idle: 8
+ # 连接池的最大数据库连接数
+ max-active: 8
+ # #连接池最大阻塞等待时间(使用负值表示没有限制)
+ max-wait: -1ms
+
+# token配置
+token:
+ # 令牌自定义标识
+ header: Authorization
+ # 令牌密钥
+ secret: abcdefghijklmnopqrstuvwxyz
+ # 令牌有效期(默认30分钟)
+ expireTime: 300
+
+
+# MyBatis Plus配置
+mybatis-plus:
+ # 搜索指定包别名
+ typeAliasesPackage: com.bs.**.domain
+ # 配置mapper的扫描,找到所有的mapper.xml映射文件
+ mapperLocations: classpath*:mapper/**/*Mapper*.xml
+ # 加载全局的配置文件
+ #configLocation: classpath:mybatis/mybatis-config.xml
+ configuration:
+ map-underscore-to-camel-case: true
+ cache-enabled: true
+ lazyLoadingEnabled: true
+ multipleResultSetsEnabled: true
+ #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用
+ use-generated-keys: true
+ default-executor-type: reuse
+mybatis-plus-join:
+ #是否打印 mybatis plus join banner 默认true
+ banner: true
+ #全局启用副表逻辑删除(默认true) 关闭后关联查询不会加副表逻辑删除
+ sub-table-logic: true
+ #拦截器MappedStatement缓存(默认true)
+ ms-cache: true
+ #表别名(默认 t)
+ table-alias: t
+ #副表逻辑删除条件的位置,支持where、on默认 where (1.4.4+)
+ logic-del-type: where
+# PageHelper分页插件
+pagehelper:
+ helperDialect: mysql
+ supportMethodsArguments: true
+ params: count=countSql
+
+# Swagger配置
+swagger:
+ # 是否开启swagger
+ enabled: true
+ # 请求前缀
+ pathMapping: /dev-api
+
+# 防止XSS攻击
+xss:
+ # 过滤开关
+ enabled: true
+ # 排除链接(多个用逗号分隔)
+ excludes: /system/notice
+ # 匹配链接
+ urlPatterns: /system/*,/monitor/*,/tool/*
diff --git a/bs-admin/src/main/resources/banner.txt b/bs-admin/src/main/resources/banner.txt
new file mode 100644
index 0000000..91a14ee
--- /dev/null
+++ b/bs-admin/src/main/resources/banner.txt
@@ -0,0 +1,24 @@
+Application Version: ${bs.version}
+Spring Boot Version: ${spring-boot.version}
+////////////////////////////////////////////////////////////////////
+// _ooOoo_ //
+// o8888888o //
+// 88" . "88 //
+// (| ^_^ |) //
+// O\ = /O //
+// ____/`---'\____ //
+// .' \\| |// `. //
+// / \\||| : |||// \ //
+// / _||||| -:- |||||- \ //
+// | | \\\ - /// | | //
+// | \_| ''\---/'' | | //
+// \ .-\__ `-` ___/-. / //
+// ___`. .' /--.--\ `. . ___ //
+// ."" '< `.___\_<|>_/___.' >'"". //
+// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
+// \ \ `-. \_ __\ /__ _/ .-` / / //
+// ========`-.____`-.___\_____/___.-`____.-'======== //
+// `=---=' //
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
+// 佛祖保佑 永不宕机 永无BUG //
+////////////////////////////////////////////////////////////////////
diff --git a/bs-admin/src/main/resources/i18n/messages.properties b/bs-admin/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..93de005
--- /dev/null
+++ b/bs-admin/src/main/resources/i18n/messages.properties
@@ -0,0 +1,38 @@
+#错误消息
+not.null=* 必须填写
+user.jcaptcha.error=验证码错误
+user.jcaptcha.expire=验证码已失效
+user.not.exists=用户不存在/密码错误
+user.password.not.match=用户不存在/密码错误
+user.password.retry.limit.count=密码输入错误{0}次
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
+user.password.delete=对不起,您的账号已被删除
+user.blocked=用户已封禁,请联系管理员
+role.blocked=角色已封禁,请联系管理员
+login.blocked=很遗憾,访问IP已被列入系统黑名单
+user.logout.success=退出成功
+
+length.not.valid=长度必须在{min}到{max}个字符之间
+
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
+user.password.not.valid=* 5-50个字符
+
+user.email.not.valid=邮箱格式错误
+user.mobile.phone.number.not.valid=手机号格式错误
+user.login.success=登录成功
+user.register.success=注册成功
+user.notfound=请重新登录
+user.forcelogout=管理员强制退出,请重新登录
+user.unknown.error=未知错误,请重新登录
+
+##文件上传消息
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小! 允许的文件最大大小是:{0}MB!
+upload.filename.exceed.length=上传的文件名最长{0}个字符
+
+##权限
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
diff --git a/bs-admin/src/main/resources/logback.xml b/bs-admin/src/main/resources/logback.xml
new file mode 100644
index 0000000..debb908
--- /dev/null
+++ b/bs-admin/src/main/resources/logback.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+ ${log.pattern}
+
+
+
+
+
+ ${log.path}/sys-info.log
+
+
+
+ ${log.path}/sys-info.%d{yyyy-MM-dd}.log
+
+ 60
+
+
+ ${log.pattern}
+
+
+
+ INFO
+
+ ACCEPT
+
+ DENY
+
+
+
+
+ ${log.path}/sys-error.log
+
+
+
+ ${log.path}/sys-error.%d{yyyy-MM-dd}.log
+
+ 60
+
+
+ ${log.pattern}
+
+
+
+ ERROR
+
+ ACCEPT
+
+ DENY
+
+
+
+
+
+ ${log.path}/sys-user.log
+
+
+ ${log.path}/sys-user.%d{yyyy-MM-dd}.log
+
+ 60
+
+
+ ${log.pattern}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bs-admin/src/main/resources/mapper/cm/CmAttachMapper.xml b/bs-admin/src/main/resources/mapper/cm/CmAttachMapper.xml
new file mode 100644
index 0000000..75deb16
--- /dev/null
+++ b/bs-admin/src/main/resources/mapper/cm/CmAttachMapper.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/bs-admin/src/main/resources/mybatis/mybatis-config.xml b/bs-admin/src/main/resources/mybatis/mybatis-config.xml
new file mode 100644
index 0000000..ac47c03
--- /dev/null
+++ b/bs-admin/src/main/resources/mybatis/mybatis-config.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bs-common/pom.xml b/bs-common/pom.xml
new file mode 100644
index 0000000..f8000de
--- /dev/null
+++ b/bs-common/pom.xml
@@ -0,0 +1,185 @@
+
+
+
+ my-base
+ com.bs
+ 3.8.6
+
+ 4.0.0
+
+ bs-common
+
+
+ common通用工具
+
+
+
+
+ org.apache.ant
+ ant
+ 1.9.15
+
+
+ com.github.jsqlparser
+ jsqlparser
+ 4.2
+
+
+
+ com.alibaba
+ transmittable-thread-local
+ 2.13.0
+
+
+
+
+
+ com.github.xiaoymin
+ knife4j-spring-boot-starter
+
+
+
+ com.alibaba
+ druid-spring-boot-starter
+
+
+
+ io.springfox
+ springfox-boot-starter
+
+
+ io.swagger
+ swagger-models
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+ com.github.yulichang
+ mybatis-plus-join-boot-starter
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.springframework
+ spring-context-support
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ org.springframework
+ spring-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ com.github.pagehelper
+ pagehelper-spring-boot-starter
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+ com.baomidou
+ dynamic-datasource-spring-boot-starter
+ 3.5.2
+
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+
+
+
+
+ commons-io
+ commons-io
+
+
+
+
+ org.apache.poi
+ poi-ooxml
+
+
+
+
+ org.yaml
+ snakeyaml
+
+
+
+
+ io.jsonwebtoken
+ jjwt
+
+
+
+
+ javax.xml.bind
+ jaxb-api
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+
+ eu.bitwalker
+ UserAgentUtils
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
+
+
diff --git a/bs-common/src/main/java/com/bs/common/annotation/Anonymous.java b/bs-common/src/main/java/com/bs/common/annotation/Anonymous.java
new file mode 100644
index 0000000..b19091c
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/Anonymous.java
@@ -0,0 +1,19 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 匿名访问不鉴权注解
+ *
+ * @author bs
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Anonymous
+{
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/DataScope.java b/bs-common/src/main/java/com/bs/common/annotation/DataScope.java
new file mode 100644
index 0000000..59b9d85
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/DataScope.java
@@ -0,0 +1,33 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 数据权限过滤注解
+ *
+ * @author bs
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataScope
+{
+ /**
+ * 部门表的别名
+ */
+ public String deptAlias() default "";
+
+ /**
+ * 用户表的别名
+ */
+ public String userAlias() default "";
+
+ /**
+ * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
+ */
+ public String permission() default "";
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/DataSource.java b/bs-common/src/main/java/com/bs/common/annotation/DataSource.java
new file mode 100644
index 0000000..a804108
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/DataSource.java
@@ -0,0 +1,28 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.bs.common.enums.DataSourceType;
+
+/**
+ * 自定义多数据源切换注解
+ *
+ * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
+ *
+ * @author bs
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DataSource
+{
+ /**
+ * 切换数据源名称
+ */
+ public DataSourceType value() default DataSourceType.MASTER;
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/Excel.java b/bs-common/src/main/java/com/bs/common/annotation/Excel.java
new file mode 100644
index 0000000..c1bb765
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/Excel.java
@@ -0,0 +1,187 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import com.bs.common.utils.poi.ExcelHandlerAdapter;
+
+/**
+ * 自定义导出Excel数据注解
+ *
+ * @author bs
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Excel
+{
+ /**
+ * 导出时在excel中排序
+ */
+ public int sort() default Integer.MAX_VALUE;
+
+ /**
+ * 导出到Excel中的名字.
+ */
+ public String name() default "";
+
+ /**
+ * 日期格式, 如: yyyy-MM-dd
+ */
+ public String dateFormat() default "";
+
+ /**
+ * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
+ */
+ public String dictType() default "";
+
+ /**
+ * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+ */
+ public String readConverterExp() default "";
+
+ /**
+ * 分隔符,读取字符串组内容
+ */
+ public String separator() default ",";
+
+ /**
+ * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
+ */
+ public int scale() default -1;
+
+ /**
+ * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
+ */
+ public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
+
+ /**
+ * 导出时在excel中每个列的高度
+ */
+ public double height() default 14;
+
+ /**
+ * 导出时在excel中每个列的宽度
+ */
+ public double width() default 16;
+
+ /**
+ * 文字后缀,如% 90 变成90%
+ */
+ public String suffix() default "";
+
+ /**
+ * 当值为空时,字段的默认值
+ */
+ public String defaultValue() default "";
+
+ /**
+ * 提示信息
+ */
+ public String prompt() default "";
+
+ /**
+ * 设置只能选择不能输入的列内容.
+ */
+ public String[] combo() default {};
+
+ /**
+ * 是否需要纵向合并单元格,应对需求:含有list集合单元格)
+ */
+ public boolean needMerge() default false;
+
+ /**
+ * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
+ */
+ public boolean isExport() default true;
+
+ /**
+ * 另一个类中的属性名称,支持多级获取,以小数点隔开
+ */
+ public String targetAttr() default "";
+
+ /**
+ * 是否自动统计数据,在最后追加一行统计数据总和
+ */
+ public boolean isStatistics() default false;
+
+ /**
+ * 导出类型(0数字 1字符串 2图片)
+ */
+ public ColumnType cellType() default ColumnType.STRING;
+
+ /**
+ * 导出列头背景颜色
+ */
+ public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
+
+ /**
+ * 导出列头字体颜色
+ */
+ public IndexedColors headerColor() default IndexedColors.WHITE;
+
+ /**
+ * 导出单元格背景颜色
+ */
+ public IndexedColors backgroundColor() default IndexedColors.WHITE;
+
+ /**
+ * 导出单元格字体颜色
+ */
+ public IndexedColors color() default IndexedColors.BLACK;
+
+ /**
+ * 导出字段对齐方式
+ */
+ public HorizontalAlignment align() default HorizontalAlignment.CENTER;
+
+ /**
+ * 自定义数据处理器
+ */
+ public Class> handler() default ExcelHandlerAdapter.class;
+
+ /**
+ * 自定义数据处理器参数
+ */
+ public String[] args() default {};
+
+ /**
+ * 字段类型(0:导出导入;1:仅导出;2:仅导入)
+ */
+ Type type() default Type.ALL;
+
+ public enum Type
+ {
+ ALL(0), EXPORT(1), IMPORT(2);
+ private final int value;
+
+ Type(int value)
+ {
+ this.value = value;
+ }
+
+ public int value()
+ {
+ return this.value;
+ }
+ }
+
+ public enum ColumnType
+ {
+ NUMERIC(0), STRING(1), IMAGE(2);
+ private final int value;
+
+ ColumnType(int value)
+ {
+ this.value = value;
+ }
+
+ public int value()
+ {
+ return this.value;
+ }
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/Excels.java b/bs-common/src/main/java/com/bs/common/annotation/Excels.java
new file mode 100644
index 0000000..1e60290
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/Excels.java
@@ -0,0 +1,18 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Excel注解集
+ *
+ * @author bs
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Excels
+{
+ public Excel[] value();
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/Log.java b/bs-common/src/main/java/com/bs/common/annotation/Log.java
new file mode 100644
index 0000000..4f5ab3a
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/Log.java
@@ -0,0 +1,51 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.bs.common.enums.BusinessType;
+import com.bs.common.enums.OperatorType;
+
+/**
+ * 自定义操作日志记录注解
+ *
+ * @author bs
+ *
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Log
+{
+ /**
+ * 模块
+ */
+ public String title() default "";
+
+ /**
+ * 功能
+ */
+ public BusinessType businessType() default BusinessType.OTHER;
+
+ /**
+ * 操作人类别
+ */
+ public OperatorType operatorType() default OperatorType.MANAGE;
+
+ /**
+ * 是否保存请求的参数
+ */
+ public boolean isSaveRequestData() default true;
+
+ /**
+ * 是否保存响应的参数
+ */
+ public boolean isSaveResponseData() default true;
+
+ /**
+ * 排除指定的请求参数
+ */
+ public String[] excludeParamNames() default {};
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/RateLimiter.java b/bs-common/src/main/java/com/bs/common/annotation/RateLimiter.java
new file mode 100644
index 0000000..7ca1de1
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/RateLimiter.java
@@ -0,0 +1,40 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import com.bs.common.constant.CacheConstants;
+import com.bs.common.enums.LimitType;
+
+/**
+ * 限流注解
+ *
+ * @author bs
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RateLimiter
+{
+ /**
+ * 限流key
+ */
+ public String key() default CacheConstants.RATE_LIMIT_KEY;
+
+ /**
+ * 限流时间,单位秒
+ */
+ public int time() default 60;
+
+ /**
+ * 限流次数
+ */
+ public int count() default 100;
+
+ /**
+ * 限流类型
+ */
+ public LimitType limitType() default LimitType.DEFAULT;
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/RepeatSubmit.java b/bs-common/src/main/java/com/bs/common/annotation/RepeatSubmit.java
new file mode 100644
index 0000000..e5b43be
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/RepeatSubmit.java
@@ -0,0 +1,31 @@
+package com.bs.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义注解防止表单重复提交
+ *
+ * @author bs
+ *
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RepeatSubmit
+{
+ /**
+ * 间隔时间(ms),小于此时间视为重复提交
+ */
+ public int interval() default 5000;
+
+ /**
+ * 提示消息
+ */
+ public String message() default "不允许重复提交,请稍候再试";
+}
diff --git a/bs-common/src/main/java/com/bs/common/annotation/TenantClear.java b/bs-common/src/main/java/com/bs/common/annotation/TenantClear.java
new file mode 100644
index 0000000..74e1bdd
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/annotation/TenantClear.java
@@ -0,0 +1,28 @@
+package com.bs.common.annotation;/**
+ * @作者 tjw
+ * @创建时间 2024-03-02-15:11
+ * @说明 :
+ */
+
+import java.lang.annotation.*;
+
+/**
+ * @author tjw
+ * @since 2023/11/21 20:41
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface TenantClear {
+
+ /**
+ * 执行该方法后,是否全局过滤租户标志
+ *
+ * true:执行完该线程之前,后续对db操作不添加租户操作
+ * false:执行完当前方法后,后续方法的db操作添加租户操作
+ *
+ *
+ * @return 默认后续不添加租户操作
+ */
+ boolean globalFlag() default true;
+}
diff --git a/bs-common/src/main/java/com/bs/common/aspectj/TenantClearAspect.java b/bs-common/src/main/java/com/bs/common/aspectj/TenantClearAspect.java
new file mode 100644
index 0000000..cf3ff3e
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/aspectj/TenantClearAspect.java
@@ -0,0 +1,45 @@
+package com.bs.common.aspectj;
+
+import com.bs.common.annotation.TenantClear;
+import lombok.SneakyThrows;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+import com.bs.common.config.TenantConfigProperties;
+
+/**
+ * @作者 tjw
+ * @创建时间 2024-03-02-15:10
+ * @说明 :
+ */
+
+@Aspect
+@Component
+public class TenantClearAspect {
+ @Autowired
+ private TenantConfigProperties tenantConfigProperties;
+
+ @SneakyThrows
+ @Around("@within(clear) || @annotation(clear)")
+ public Object around(ProceedingJoinPoint point, TenantClear clear) {
+ // 如果在类上,而不是在方法上,则为空
+ if (clear == null) {
+ Class> aClass = point.getTarget().getClass();
+ clear = AnnotationUtils.findAnnotation(aClass, TenantClear.class);
+ }
+ if (clear != null) {
+ if(clear.globalFlag()){
+ tenantConfigProperties.setTenantSkip();
+ }
+ }
+ Object result = point.proceed();
+ if (!clear.globalFlag()) {
+ tenantConfigProperties.clearSkip();
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/bs-common/src/main/java/com/bs/common/config/BsConfig.java b/bs-common/src/main/java/com/bs/common/config/BsConfig.java
new file mode 100644
index 0000000..7fc5486
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/config/BsConfig.java
@@ -0,0 +1,220 @@
+package com.bs.common.config;
+
+import com.bs.common.utils.OsInfoUtil;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 读取项目相关配置
+ *
+ * @author bs
+ */
+@Component
+@ConfigurationProperties(prefix = "bs")
+public class BsConfig
+{
+ /** 项目名称 */
+ private String name;
+
+ /**
+ * 维护租户列名称
+ */
+ private static String column = "tenant_id";
+
+ /**
+ * 多租户的数据表集合(不忽略的表)
+ */
+ private static Set noIgnoreTables;
+ /** 版本 */
+ private String version;
+
+ /** 版权年份 */
+ private String copyrightYear;
+
+ /** 实例演示开关 */
+ private boolean demoEnabled;
+
+ /** 上传路径 */
+ private static String profile;
+
+ /** 获取地址开关 */
+ private static boolean addressEnabled;
+
+ /** 验证码类型 */
+ private static String captchaType;
+
+ /**
+ * windows附件地址
+ */
+ private static String winLocalFilePath;
+ /**
+ * linux附件地址
+ */
+ private static String linuxLocalFilePath;
+ /**
+ * mac附件地址
+ */
+ private static String macLocalFilePath;
+
+ public void setWinLocalFilePath(String winLocalFilePath) {
+ BsConfig.winLocalFilePath = winLocalFilePath;
+ }
+
+ public void setLinuxLocalFilePath(String linuxLocalFilePath) {
+ BsConfig.linuxLocalFilePath = linuxLocalFilePath;
+ }
+
+ public void setMacLocalFilePath(String macLocalFilePath) {
+ BsConfig.macLocalFilePath = macLocalFilePath;
+ }
+
+ public static String getWinLocalFilePath() {
+ return winLocalFilePath;
+ }
+
+ public static String getLinuxLocalFilePath() {
+ return linuxLocalFilePath;
+ }
+ public static String getMacLocalFilePath() {
+ return macLocalFilePath;
+ }
+
+
+
+ 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)
+ {
+ BsConfig.profile = profile;
+ }
+
+ public static String getColumn() {
+ return column;
+ }
+
+ public static void setColumn(String column) {
+ BsConfig.column = column;
+ }
+ public static Set getTables() {
+ return getNoIgnoreTables();
+ }
+ public static Set getNoIgnoreTables() {
+ return noIgnoreTables;
+ }
+
+ public static void setNoIgnoreTables(Set noIgnoreTables) {
+ BsConfig.noIgnoreTables = noIgnoreTables;
+ }
+
+ public static boolean isAddressEnabled()
+ {
+ return addressEnabled;
+ }
+
+ public void setAddressEnabled(boolean addressEnabled)
+ {
+ BsConfig.addressEnabled = addressEnabled;
+ }
+
+ public static String getCaptchaType() {
+ return captchaType;
+ }
+
+ public void setCaptchaType(String captchaType) {
+ BsConfig.captchaType = captchaType;
+ }
+
+ /**
+ * 获取导入上传路径
+ */
+ public static String getImportPath()
+ {
+ return getFilePath() + "/import";
+ }
+
+ /**
+ * 获取头像上传路径
+ */
+ public static String getAvatarPath()
+ {
+ return getFilePath() + "/avatar";
+ }
+
+ /**
+ * 获取下载路径
+ */
+ public static String getDownloadPath()
+ {
+ return getFilePath() + "/download/";
+ }
+
+ /**
+ * 获取上传路径
+ */
+ public static String getUploadPath()
+ {
+ return getFilePath() + "/upload";
+ }
+
+
+ /**
+ * 根据系统切换存放附件绝对地址
+ *
+ * @return 存放附件绝对地址
+ */
+ public static String getFilePath() {
+ if (OsInfoUtil.isLinux()) {
+ return getLinuxLocalFilePath();
+ }
+ if (OsInfoUtil.isMacOS() || OsInfoUtil.isMacOSX()) {
+ return getMacLocalFilePath();
+ }
+ return getWinLocalFilePath();
+ }
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/config/TenantConfigProperties.java b/bs-common/src/main/java/com/bs/common/config/TenantConfigProperties.java
new file mode 100644
index 0000000..01b34dc
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/config/TenantConfigProperties.java
@@ -0,0 +1,82 @@
+package com.bs.common.config;
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+@Component
+@ConfigurationProperties(prefix = "bs.tenant")
+public class TenantConfigProperties {
+
+ /**
+ * 维护租户列名称
+ */
+ private String column;
+
+ /**
+ * 多租户的数据表集合(不忽略的表)
+ */
+ private Set noIgnoreTables;
+
+
+ private boolean openTenant;
+
+ /**
+ * 租户过滤标识
+ */
+ private final ThreadLocal TENANT_SKIP = new TransmittableThreadLocal<>();
+ /**
+ * 设置是否过滤的标识
+ */
+ public void setTenantSkip() {
+ TENANT_SKIP.set(Boolean.TRUE);
+ }
+ /**
+ * 获取是否跳过租户过滤的标识
+ * @return true-过滤 false-不过滤
+ */
+ public Boolean getTenantSkip() {
+ return TENANT_SKIP.get() != null && TENANT_SKIP.get();
+ }
+
+ /**
+ * 清空租户过滤标识
+ */
+ public void clearSkip(){
+ TENANT_SKIP.remove();
+ }
+
+
+ public String getColumn() {
+ return column;
+ }
+
+ public void setColumn(String column) {
+ this.column = column;
+ }
+
+
+ public Boolean getOpenTenant() {
+ return openTenant;
+ }
+
+ public void setOpenTenant(Boolean openTenant) {
+ this.openTenant = openTenant;
+ }
+
+
+ public Set getNoIgnoreTables() {
+ return noIgnoreTables;
+ }
+
+ public void setNoIgnoreTables(Set noIgnoreTables) {
+ this.noIgnoreTables = noIgnoreTables;
+ }
+
+ public Set getTables() {
+ return getNoIgnoreTables();
+ }
+}
+
diff --git a/bs-common/src/main/java/com/bs/common/constant/CacheConstants.java b/bs-common/src/main/java/com/bs/common/constant/CacheConstants.java
new file mode 100644
index 0000000..be6bb6a
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/constant/CacheConstants.java
@@ -0,0 +1,44 @@
+package com.bs.common.constant;
+
+/**
+ * 缓存的key 常量
+ *
+ * @author bs
+ */
+public class CacheConstants
+{
+ /**
+ * 登录用户 redis key
+ */
+ public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+ /**
+ * 验证码 redis key
+ */
+ public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+ /**
+ * 参数管理 cache key
+ */
+ public static final String SYS_CONFIG_KEY = "sys_config:";
+
+ /**
+ * 字典管理 cache key
+ */
+ public static final String SYS_DICT_KEY = "sys_dict:";
+
+ /**
+ * 防重提交 redis key
+ */
+ public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+
+ /**
+ * 限流 redis key
+ */
+ public static final String RATE_LIMIT_KEY = "rate_limit:";
+
+ /**
+ * 登录账户密码错误次数 redis key
+ */
+ public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
+}
diff --git a/bs-common/src/main/java/com/bs/common/constant/Constants.java b/bs-common/src/main/java/com/bs/common/constant/Constants.java
new file mode 100644
index 0000000..6036fc9
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/constant/Constants.java
@@ -0,0 +1,142 @@
+package com.bs.common.constant;
+
+import io.jsonwebtoken.Claims;
+
+/**
+ * 通用常量信息
+ *
+ * @author bs
+ */
+public class Constants
+{
+ /**
+ * UTF-8 字符集
+ */
+ public static final String UTF8 = "UTF-8";
+
+ /**
+ * GBK 字符集
+ */
+ public static final String GBK = "GBK";
+
+ /**
+ * www主域
+ */
+ public static final String WWW = "www.";
+
+ /**
+ * http请求
+ */
+ public static final String HTTP = "http://";
+
+ /**
+ * https请求
+ */
+ public static final String HTTPS = "https://";
+
+ /**
+ * 通用成功标识
+ */
+ 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 REGISTER = "Register";
+
+ /**
+ * 登录失败
+ */
+ public static final String LOGIN_FAIL = "Error";
+
+ /**
+ * 验证码有效期(分钟)
+ */
+ public static final Integer CAPTCHA_EXPIRATION = 2;
+
+ /**
+ * 令牌
+ */
+ public static final String TOKEN = "token";
+
+ /**
+ * 令牌前缀
+ */
+ public static final String TOKEN_PREFIX = "Bearer ";
+
+ /**
+ * 令牌前缀
+ */
+ public static final String LOGIN_USER_KEY = "login_user_key";
+
+ /**
+ * 用户ID
+ */
+ public static final String JWT_USERID = "userid";
+
+ /**
+ * 用户名称
+ */
+ public static final String JWT_USERNAME = Claims.SUBJECT;
+
+ /**
+ * 用户头像
+ */
+ public static final String JWT_AVATAR = "avatar";
+
+ /**
+ * 创建时间
+ */
+ public static final String JWT_CREATED = "created";
+
+ /**
+ * 用户权限
+ */
+ public static final String JWT_AUTHORITIES = "authorities";
+
+ /**
+ * 资源映射路径 前缀
+ */
+ public static final String RESOURCE_PREFIX = "/profile";
+
+ /**
+ * RMI 远程方法调用
+ */
+ public static final String LOOKUP_RMI = "rmi:";
+
+ /**
+ * LDAP 远程方法调用
+ */
+ public static final String LOOKUP_LDAP = "ldap:";
+
+ /**
+ * LDAPS 远程方法调用
+ */
+ public static final String LOOKUP_LDAPS = "ldaps:";
+
+ /**
+ * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
+ */
+ public static final String[] JOB_WHITELIST_STR = { "com.bs" };
+
+ /**
+ * 定时任务违规的字符
+ */
+ public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
+ "org.springframework", "org.apache", "com.bs.common.utils.file", "com.bs.common.config" };
+}
diff --git a/bs-common/src/main/java/com/bs/common/constant/GenConstants.java b/bs-common/src/main/java/com/bs/common/constant/GenConstants.java
new file mode 100644
index 0000000..8b7d8e1
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/constant/GenConstants.java
@@ -0,0 +1,117 @@
+package com.bs.common.constant;
+
+/**
+ * 代码生成通用常量
+ *
+ * @author bs
+ */
+public class GenConstants
+{
+ /** 单表(增删改查) */
+ public static final String TPL_CRUD = "crud";
+
+ /** 树表(增删改查) */
+ public static final String TPL_TREE = "tree";
+
+ /** 主子表(增删改查) */
+ public static final String TPL_SUB = "sub";
+
+ /** 树编码字段 */
+ public static final String TREE_CODE = "treeCode";
+
+ /** 树父编码字段 */
+ public static final String TREE_PARENT_CODE = "treeParentCode";
+
+ /** 树名称字段 */
+ public static final String TREE_NAME = "treeName";
+
+ /** 上级菜单ID字段 */
+ public static final String PARENT_MENU_ID = "parentMenuId";
+
+ /** 上级菜单名称字段 */
+ public static final String PARENT_MENU_NAME = "parentMenuName";
+
+ /** 数据库字符串类型 */
+ public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
+
+ /** 数据库文本类型 */
+ public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
+
+ /** 数据库时间类型 */
+ public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
+
+ /** 数据库数字类型 */
+ public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer",
+ "bit", "bigint", "float", "double", "decimal" };
+
+ /** 页面不需要编辑字段 */
+ public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };
+
+ /** 页面不需要显示的列表字段 */
+ public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by",
+ "update_time" };
+
+ /** 页面不需要查询字段 */
+ public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by",
+ "update_time", "remark" };
+
+ /** Entity基类字段 */
+ public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime"};
+
+ /** Tree基类字段 */
+ public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" };
+
+ /** 文本框 */
+ public static final String HTML_INPUT = "input";
+
+ /** 文本域 */
+ public static final String HTML_TEXTAREA = "textarea";
+
+ /** 下拉框 */
+ public static final String HTML_SELECT = "select";
+
+ /** 单选框 */
+ public static final String HTML_RADIO = "radio";
+
+ /** 复选框 */
+ public static final String HTML_CHECKBOX = "checkbox";
+
+ /** 日期控件 */
+ public static final String HTML_DATETIME = "datetime";
+
+ /** 图片上传控件 */
+ public static final String HTML_IMAGE_UPLOAD = "imageUpload";
+
+ /** 文件上传控件 */
+ public static final String HTML_FILE_UPLOAD = "fileUpload";
+
+ /** 富文本控件 */
+ public static final String HTML_EDITOR = "editor";
+
+ /** 字符串类型 */
+ public static final String TYPE_STRING = "String";
+
+ /** 整型 */
+ public static final String TYPE_INTEGER = "Integer";
+
+ /** 长整型 */
+ public static final String TYPE_LONG = "Long";
+
+ /** 浮点型 */
+ public static final String TYPE_DOUBLE = "Double";
+
+ /** 高精度计算类型 */
+ public static final String TYPE_BIGDECIMAL = "BigDecimal";
+
+ /** 时间类型 */
+ public static final String TYPE_DATE = "Date";
+
+ /** 模糊查询 */
+ public static final String QUERY_LIKE = "LIKE";
+
+ /** 相等查询 */
+ public static final String QUERY_EQ = "EQ";
+
+ /** 需要 */
+ public static final String REQUIRE = "1";
+}
diff --git a/bs-common/src/main/java/com/bs/common/constant/HttpStatus.java b/bs-common/src/main/java/com/bs/common/constant/HttpStatus.java
new file mode 100644
index 0000000..64319e7
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/constant/HttpStatus.java
@@ -0,0 +1,94 @@
+package com.bs.common.constant;
+
+/**
+ * 返回状态码
+ *
+ * @author bs
+ */
+public class HttpStatus
+{
+ /**
+ * 操作成功
+ */
+ public static final int SUCCESS = 200;
+
+ /**
+ * 对象创建成功
+ */
+ public static final int CREATED = 201;
+
+ /**
+ * 请求已经被接受
+ */
+ public static final int ACCEPTED = 202;
+
+ /**
+ * 操作已经执行成功,但是没有返回数据
+ */
+ public static final int NO_CONTENT = 204;
+
+ /**
+ * 资源已被移除
+ */
+ public static final int MOVED_PERM = 301;
+
+ /**
+ * 重定向
+ */
+ public static final int SEE_OTHER = 303;
+
+ /**
+ * 资源没有被修改
+ */
+ public static final int NOT_MODIFIED = 304;
+
+ /**
+ * 参数列表错误(缺少,格式不匹配)
+ */
+ public static final int BAD_REQUEST = 400;
+
+ /**
+ * 未授权
+ */
+ public static final int UNAUTHORIZED = 401;
+
+ /**
+ * 访问受限,授权过期
+ */
+ public static final int FORBIDDEN = 403;
+
+ /**
+ * 资源,服务未找到
+ */
+ public static final int NOT_FOUND = 404;
+
+ /**
+ * 不允许的http方法
+ */
+ public static final int BAD_METHOD = 405;
+
+ /**
+ * 资源冲突,或者资源被锁
+ */
+ public static final int CONFLICT = 409;
+
+ /**
+ * 不支持的数据,媒体类型
+ */
+ public static final int UNSUPPORTED_TYPE = 415;
+
+ /**
+ * 系统内部错误
+ */
+ public static final int ERROR = 500;
+
+ /**
+ * 接口未实现
+ */
+ public static final int NOT_IMPLEMENTED = 501;
+
+ /**
+ * 系统警告消息
+ */
+ public static final int WARN = 601;
+}
diff --git a/bs-common/src/main/java/com/bs/common/constant/ScheduleConstants.java b/bs-common/src/main/java/com/bs/common/constant/ScheduleConstants.java
new file mode 100644
index 0000000..1680754
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/constant/ScheduleConstants.java
@@ -0,0 +1,50 @@
+package com.bs.common.constant;
+
+/**
+ * 任务调度通用常量
+ *
+ * @author bs
+ */
+public class ScheduleConstants
+{
+ public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
+
+ /** 执行目标key */
+ public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
+
+ /** 默认 */
+ public static final String MISFIRE_DEFAULT = "0";
+
+ /** 立即触发执行 */
+ public static final String MISFIRE_IGNORE_MISFIRES = "1";
+
+ /** 触发一次执行 */
+ public static final String MISFIRE_FIRE_AND_PROCEED = "2";
+
+ /** 不触发立即执行 */
+ public static final String MISFIRE_DO_NOTHING = "3";
+
+ public enum Status
+ {
+ /**
+ * 正常
+ */
+ NORMAL("0"),
+ /**
+ * 暂停
+ */
+ PAUSE("1");
+
+ private String value;
+
+ private Status(String value)
+ {
+ this.value = value;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/constant/UserConstants.java b/bs-common/src/main/java/com/bs/common/constant/UserConstants.java
new file mode 100644
index 0000000..37053a7
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/constant/UserConstants.java
@@ -0,0 +1,78 @@
+package com.bs.common.constant;
+
+/**
+ * 用户常量信息
+ *
+ * @author bs
+ */
+public class UserConstants
+{
+ /**
+ * 平台内系统用户的唯一标志
+ */
+ public static final String SYS_USER = "SYS_USER";
+
+ /** 正常状态 */
+ public static final String NORMAL = "0";
+
+ /** 异常状态 */
+ public static final String EXCEPTION = "1";
+
+ /** 用户封禁状态 */
+ public static final String USER_DISABLE = "1";
+
+ /** 角色封禁状态 */
+ public static final String ROLE_DISABLE = "1";
+
+ /** 部门正常状态 */
+ public static final String DEPT_NORMAL = "0";
+
+ /** 部门停用状态 */
+ public static final String DEPT_DISABLE = "1";
+
+ /** 字典正常状态 */
+ public static final String DICT_NORMAL = "0";
+
+ /** 是否为系统默认(是) */
+ public static final String YES = "Y";
+
+ /** 是否菜单外链(是) */
+ public static final String YES_FRAME = "0";
+
+ /** 是否菜单外链(否) */
+ public static final String NO_FRAME = "1";
+
+ /** 菜单类型(目录) */
+ public static final String TYPE_DIR = "M";
+
+ /** 菜单类型(菜单) */
+ public static final String TYPE_MENU = "C";
+
+ /** 菜单类型(按钮) */
+ public static final String TYPE_BUTTON = "F";
+
+ /** Layout组件标识 */
+ public final static String LAYOUT = "Layout";
+
+ /** ParentView组件标识 */
+ public final static String PARENT_VIEW = "ParentView";
+
+ /** InnerLink组件标识 */
+ public final static String INNER_LINK = "InnerLink";
+
+ /** 校验是否唯一的返回标识 */
+ public final static boolean UNIQUE = true;
+ public final static boolean NOT_UNIQUE = false;
+
+ /**
+ * 用户名长度限制
+ */
+ public static final int USERNAME_MIN_LENGTH = 2;
+ public static final int USERNAME_MAX_LENGTH = 20;
+
+ /**
+ * 密码长度限制
+ */
+ public static final int PASSWORD_MIN_LENGTH = 5;
+ public static final int PASSWORD_MAX_LENGTH = 20;
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/controller/BaseController.java b/bs-common/src/main/java/com/bs/common/core/controller/BaseController.java
new file mode 100644
index 0000000..64ac5f9
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/controller/BaseController.java
@@ -0,0 +1,216 @@
+package com.bs.common.core.controller;
+
+import com.bs.common.constant.HttpStatus;
+import com.bs.common.core.domain.AjaxResult;
+import com.bs.common.core.domain.model.LoginUser;
+import com.bs.common.core.page.PageDomain;
+import com.bs.common.core.page.TableDataInfo;
+import com.bs.common.core.page.TableSupport;
+import com.bs.common.utils.DateUtils;
+import com.bs.common.utils.PageUtils;
+import com.bs.common.utils.SecurityUtils;
+import com.bs.common.utils.StringUtils;
+import com.bs.common.utils.sql.SqlUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+import java.beans.PropertyEditorSupport;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * web层通用数据处理
+ *
+ * @author bs
+ */
+public class BaseController
+{
+ protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /**
+ * 将前台传递过来的日期格式的字符串,自动转化为Date类型
+ */
+ @InitBinder
+ public void initBinder(WebDataBinder binder)
+ {
+ // Date 类型转换
+ binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
+ {
+ @Override
+ public void setAsText(String text)
+ {
+ setValue(DateUtils.parseDate(text));
+ }
+ });
+ }
+
+ /**
+ * 设置请求分页数据
+ */
+ protected void startPage()
+ {
+ PageUtils.startPage();
+ }
+
+ /**
+ * 设置请求排序数据
+ */
+ protected void startOrderBy()
+ {
+ PageDomain pageDomain = TableSupport.buildPageRequest();
+ if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
+ {
+ String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+ PageHelper.orderBy(orderBy);
+ }
+ }
+
+ /**
+ * 清理分页的线程变量
+ */
+ protected void clearPage()
+ {
+ PageUtils.clearPage();
+ }
+
+ /**
+ * 响应请求分页数据
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected TableDataInfo getDataTable(List> list)
+ {
+ TableDataInfo rspData = new TableDataInfo();
+ rspData.setCode(HttpStatus.SUCCESS);
+ rspData.setMsg("查询成功");
+ rspData.setRows(list);
+ rspData.setTotal(new PageInfo(list).getTotal());
+ return rspData;
+ }
+ /**
+ * 响应请求分页数据
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected TableDataInfo getDataTable(List> list,long total)
+ {
+ TableDataInfo rspData = new TableDataInfo();
+ rspData.setCode(HttpStatus.SUCCESS);
+ rspData.setMsg("查询成功");
+ rspData.setRows(list);
+ rspData.setTotal(total);
+ return rspData;
+ }
+
+ /**
+ * 返回成功
+ */
+ public AjaxResult success()
+ {
+ return AjaxResult.success();
+ }
+
+ /**
+ * 返回失败消息
+ */
+ public AjaxResult error()
+ {
+ return AjaxResult.error();
+ }
+
+ /**
+ * 返回成功消息
+ */
+ public AjaxResult success(String message)
+ {
+ return AjaxResult.success(message);
+ }
+
+ /**
+ * 返回成功消息
+ */
+ public AjaxResult success(Object data)
+ {
+ return AjaxResult.success(data);
+ }
+
+ /**
+ * 返回失败消息
+ */
+ public AjaxResult error(String message)
+ {
+ return AjaxResult.error(message);
+ }
+
+ /**
+ * 返回警告消息
+ */
+ public AjaxResult warn(String message)
+ {
+ return AjaxResult.warn(message);
+ }
+
+ /**
+ * 响应返回结果
+ *
+ * @param rows 影响行数
+ * @return 操作结果
+ */
+ protected AjaxResult toAjax(int rows)
+ {
+ return rows > 0 ? AjaxResult.success() : AjaxResult.error();
+ }
+
+ /**
+ * 响应返回结果
+ *
+ * @param result 结果
+ * @return 操作结果
+ */
+ protected AjaxResult toAjax(boolean result)
+ {
+ return result ? success() : error();
+ }
+
+ /**
+ * 页面跳转
+ */
+ public String redirect(String url)
+ {
+ return StringUtils.format("redirect:{}", url);
+ }
+
+ /**
+ * 获取用户缓存信息
+ */
+ public LoginUser getLoginUser()
+ {
+ return SecurityUtils.getLoginUser();
+ }
+
+ /**
+ * 获取登录用户id
+ */
+ public Long getUserId()
+ {
+ return getLoginUser().getUserId();
+ }
+
+ /**
+ * 获取登录部门id
+ */
+ public Long getDeptId()
+ {
+ return getLoginUser().getDeptId();
+ }
+
+ /**
+ * 获取登录用户名
+ */
+ public Long getUsername()
+ {
+ return getLoginUser().getUserId();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/AjaxResult.java b/bs-common/src/main/java/com/bs/common/core/domain/AjaxResult.java
new file mode 100644
index 0000000..18f2796
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/AjaxResult.java
@@ -0,0 +1,206 @@
+package com.bs.common.core.domain;
+
+
+import com.bs.common.constant.HttpStatus;
+import com.bs.common.utils.StringUtils;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ *
+ * @author ruoyi
+ */
+public class AjaxResult extends HashMap {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 状态码
+ */
+ public static final String CODE_TAG = "code";
+
+ private static final String STATE = "state";
+ private static final String STATE_OK = "ok";
+ private static final String STATE_FAIL = "fail";
+ /**
+ * 返回内容
+ */
+ public static final String MSG_TAG = "msg";
+
+ /**
+ * 数据对象
+ */
+ public static final String DATA_TAG = "data";
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+ */
+ public AjaxResult() {
+ }
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ */
+ public AjaxResult(int code, String msg) {
+ super.put(CODE_TAG, code);
+ super.put(MSG_TAG, msg);
+ }
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ * @param data 数据对象
+ */
+ public AjaxResult(int code, String msg, Object data, String state) {
+ super.put(STATE, state);
+ super.put(CODE_TAG, code);
+ super.put(MSG_TAG, msg);
+ if (StringUtils.isNotNull(data)) {
+ super.put(DATA_TAG, data);
+ }
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success() {
+ return AjaxResult.success("操作成功");
+ }
+
+ /**
+ * 返回成功数据
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success(Object data) {
+ return AjaxResult.success("操作成功", data);
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 返回内容
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg) {
+ return AjaxResult.success(msg, null);
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg, Object data) {
+ //所有的
+ return new AjaxResult(HttpStatus.SUCCESS, msg, data,STATE_OK);
+ }
+
+ /**
+ * 返回警告消息
+ *
+ * @param msg 返回内容
+ * @return 警告消息
+ */
+ public static AjaxResult warn(String msg) {
+ return AjaxResult.warn(msg, null);
+ }
+
+ /**
+ * 返回警告消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 警告消息
+ */
+ public static AjaxResult warn(String msg, Object data) {
+ return new AjaxResult(HttpStatus.WARN, msg, data,STATE_FAIL);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @return
+ */
+ public static AjaxResult error() {
+ return AjaxResult.error("操作失败");
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 返回内容
+ * @return 错误消息
+ */
+ public static AjaxResult error(String msg) {
+ return AjaxResult.error(msg, null);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 错误消息
+ */
+ public static AjaxResult error(String msg, Object data) {
+ return new AjaxResult(HttpStatus.ERROR, msg, data,STATE_FAIL);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ * @return 错误消息
+ */
+ public static AjaxResult error(int code, String msg) {
+ return new AjaxResult(code, msg, null,STATE_FAIL);
+ }
+
+ /**
+ * 方便链式调用
+ *
+ * @param key 键
+ * @param value 值
+ * @return 数据对象
+ */
+ @Override
+ public AjaxResult put(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+
+
+ public boolean isOk() {
+ Object state = get(STATE);
+ if (STATE_OK.equals(state)) {
+ return true;
+ }
+ if (STATE_FAIL.equals(state)) {
+ return false;
+ }
+ throw new IllegalStateException("调用 isOk() 之前,必须先调用 ok()、fail() 或者 setOk()、setFail() 方法");
+ }
+
+ public boolean isFail() {
+ Object state = get(STATE);
+ if (STATE_FAIL.equals(state)) {
+ return true;
+ }
+ if (STATE_OK.equals(state)) {
+ return false;
+ }
+
+ throw new IllegalStateException("调用 isFail() 之前,必须先调用 ok()、fail() 或者 setOk()、setFail() 方法");
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/BaseEntity.java b/bs-common/src/main/java/com/bs/common/core/domain/BaseEntity.java
new file mode 100644
index 0000000..564484b
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/BaseEntity.java
@@ -0,0 +1,93 @@
+package com.bs.common.core.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Entity基类
+ *
+ * @author bs
+ */
+@Data
+@JsonInclude(value= JsonInclude.Include.NON_NULL)
+public class BaseEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * 搜索值
+ */
+ @JsonIgnore
+ @TableField(exist = false)
+ private String searchValue;
+
+ private Long tenantId;
+
+ /**
+ * 创建部门
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Long createDept;
+
+ /**
+ * 创建者
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Long createBy;
+
+ /**
+ * 创建时间
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updateBy;
+
+ /**
+ * 更新时间
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+ /**
+ * 逻辑删除标示
+ */
+ //做删除操作的时候,会变成update,del_flag = '2'
+ @TableLogic(value = "0", delval = "2")
+ @TableField(fill = FieldFill.INSERT)
+ @JsonIgnore
+ private String delFlag;
+
+
+ /**
+ * 创建部门(查询机构下的部门数据)
+ */
+ @TableField(exist = false)
+ private Long jgCreateDept;
+ /**
+ * 权限sql拼接
+ */
+ @TableField(exist = false)
+ private String scopeSql;
+
+ /**
+ * 请求参数
+ */
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ @TableField(exist = false)
+ private Map params = new HashMap<>();
+
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/R.java b/bs-common/src/main/java/com/bs/common/core/domain/R.java
new file mode 100644
index 0000000..86924b2
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/R.java
@@ -0,0 +1,115 @@
+package com.bs.common.core.domain;
+
+import java.io.Serializable;
+import com.bs.common.constant.HttpStatus;
+
+/**
+ * 响应信息主体
+ *
+ * @author bs
+ */
+public class R implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 成功 */
+ public static final int SUCCESS = HttpStatus.SUCCESS;
+
+ /** 失败 */
+ public static final int FAIL = HttpStatus.ERROR;
+
+ private int code;
+
+ private String msg;
+
+ private T data;
+
+ public static R ok()
+ {
+ return restResult(null, SUCCESS, "操作成功");
+ }
+
+ public static R ok(T data)
+ {
+ return restResult(data, SUCCESS, "操作成功");
+ }
+
+ public static R ok(T data, String msg)
+ {
+ return restResult(data, SUCCESS, msg);
+ }
+
+ public static R fail()
+ {
+ return restResult(null, FAIL, "操作失败");
+ }
+
+ public static R fail(String msg)
+ {
+ return restResult(null, FAIL, msg);
+ }
+
+ public static R fail(T data)
+ {
+ return restResult(data, FAIL, "操作失败");
+ }
+
+ public static R fail(T data, String msg)
+ {
+ return restResult(data, FAIL, msg);
+ }
+
+ public static R fail(int code, String msg)
+ {
+ return restResult(null, code, msg);
+ }
+
+ private static R restResult(T data, int code, String msg)
+ {
+ R apiResult = new R<>();
+ apiResult.setCode(code);
+ apiResult.setData(data);
+ apiResult.setMsg(msg);
+ return apiResult;
+ }
+
+ public int getCode()
+ {
+ return code;
+ }
+
+ public void setCode(int code)
+ {
+ this.code = code;
+ }
+
+ public String getMsg()
+ {
+ return msg;
+ }
+
+ public void setMsg(String msg)
+ {
+ this.msg = msg;
+ }
+
+ public T getData()
+ {
+ return data;
+ }
+
+ public void setData(T data)
+ {
+ this.data = data;
+ }
+
+ public static Boolean isError(R ret)
+ {
+ return !isSuccess(ret);
+ }
+
+ public static Boolean isSuccess(R ret)
+ {
+ return R.SUCCESS == ret.getCode();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/TreeEntity.java b/bs-common/src/main/java/com/bs/common/core/domain/TreeEntity.java
new file mode 100644
index 0000000..f3f5386
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/TreeEntity.java
@@ -0,0 +1,79 @@
+package com.bs.common.core.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tree基类
+ *
+ * @author bs
+ */
+public class TreeEntity extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 父菜单名称 */
+ private String parentName;
+
+ /** 父菜单ID */
+ private Long parentId;
+
+ /** 显示顺序 */
+ private Integer orderNum;
+
+ /** 祖级列表 */
+ private String ancestors;
+
+ /** 子部门 */
+ private List> children = new ArrayList<>();
+
+ public String getParentName()
+ {
+ return parentName;
+ }
+
+ public void setParentName(String parentName)
+ {
+ this.parentName = parentName;
+ }
+
+ public Long getParentId()
+ {
+ return parentId;
+ }
+
+ public void setParentId(Long parentId)
+ {
+ this.parentId = parentId;
+ }
+
+ public Integer getOrderNum()
+ {
+ return orderNum;
+ }
+
+ public void setOrderNum(Integer orderNum)
+ {
+ this.orderNum = orderNum;
+ }
+
+ public String getAncestors()
+ {
+ return ancestors;
+ }
+
+ public void setAncestors(String ancestors)
+ {
+ this.ancestors = ancestors;
+ }
+
+ public List> getChildren()
+ {
+ return children;
+ }
+
+ public void setChildren(List> children)
+ {
+ this.children = children;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/TreeSelect.java b/bs-common/src/main/java/com/bs/common/core/domain/TreeSelect.java
new file mode 100644
index 0000000..dc83765
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/TreeSelect.java
@@ -0,0 +1,77 @@
+package com.bs.common.core.domain;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.bs.common.core.domain.entity.SysDept;
+import com.bs.common.core.domain.entity.SysMenu;
+
+/**
+ * Treeselect树结构实体类
+ *
+ * @author bs
+ */
+public class TreeSelect implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 节点ID */
+ private Long id;
+
+ /** 节点名称 */
+ private String label;
+
+ /** 子节点 */
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ private List children;
+
+ public TreeSelect()
+ {
+
+ }
+
+ public TreeSelect(SysDept dept)
+ {
+ this.id = dept.getDeptId();
+ this.label = dept.getDeptName();
+ this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
+ }
+
+ public TreeSelect(SysMenu menu)
+ {
+ this.id = menu.getMenuId();
+ this.label = menu.getMenuName();
+ this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+
+ public void setId(Long id)
+ {
+ this.id = id;
+ }
+
+ public String getLabel()
+ {
+ return label;
+ }
+
+ public void setLabel(String label)
+ {
+ this.label = label;
+ }
+
+ public List getChildren()
+ {
+ return children;
+ }
+
+ public void setChildren(List children)
+ {
+ this.children = children;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDept.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDept.java
new file mode 100644
index 0000000..6a83c55
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDept.java
@@ -0,0 +1,200 @@
+package com.bs.common.core.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.bs.common.core.domain.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 部门表 sys_dept
+ *
+ * @author bs
+ */
+@Data
+public class SysDept extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 部门ID
+ */
+ @TableId(value = "dept_id",type = IdType.AUTO)
+ private Long deptId;
+
+ /**
+ * 父部门ID
+ */
+ private Long parentId;
+
+ /**
+ * 祖级列表
+ */
+ private String ancestors;
+
+ /**
+ * 部门名称
+ */
+ private String deptName;
+
+ /**
+ * 显示顺序
+ */
+ private Integer orderNum;
+
+ /**
+ * 负责人
+ */
+ private String leader;
+
+ /**
+ * 联系电话
+ */
+ private String phone;
+
+ /**
+ * 邮箱
+ */
+ private String email;
+
+ /**
+ * 部门状态:0正常,1停用
+ */
+ private String status;
+
+
+ /**
+ * 父部门名称
+ */
+ @TableField(exist = false)
+ private String parentName;
+
+ /**
+ * 子部门
+ */
+ @TableField(exist = false)
+ private List children = new ArrayList();
+
+ public Long getDeptId() {
+ return deptId;
+ }
+
+ public void setDeptId(Long deptId) {
+ this.deptId = deptId;
+ }
+
+ public Long getParentId() {
+ return parentId;
+ }
+
+ public void setParentId(Long parentId) {
+ this.parentId = parentId;
+ }
+
+ public String getAncestors() {
+ return ancestors;
+ }
+
+ public void setAncestors(String ancestors) {
+ this.ancestors = ancestors;
+ }
+
+ @NotBlank(message = "部门名称不能为空")
+ @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
+ public String getDeptName() {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName) {
+ this.deptName = deptName;
+ }
+
+ @NotNull(message = "显示顺序不能为空")
+ public Integer getOrderNum() {
+ return orderNum;
+ }
+
+ public void setOrderNum(Integer orderNum) {
+ this.orderNum = orderNum;
+ }
+
+ public String getLeader() {
+ return leader;
+ }
+
+ public void setLeader(String leader) {
+ this.leader = leader;
+ }
+
+ @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
+ @Email(message = "邮箱格式不正确")
+ @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+
+ public String getParentName() {
+ return parentName;
+ }
+
+ public void setParentName(String parentName) {
+ this.parentName = parentName;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+ .append("deptId", getDeptId())
+ .append("parentId", getParentId())
+ .append("ancestors", getAncestors())
+ .append("deptName", getDeptName())
+ .append("orderNum", getOrderNum())
+ .append("leader", getLeader())
+ .append("phone", getPhone())
+ .append("email", getEmail())
+ .append("status", getStatus())
+ .append("delFlag", getDelFlag())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .toString();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDictData.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDictData.java
new file mode 100644
index 0000000..4698aef
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDictData.java
@@ -0,0 +1,67 @@
+package com.bs.common.core.domain.entity;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.bs.common.annotation.Excel;
+import com.bs.common.annotation.Excel.ColumnType;
+import com.bs.common.constant.UserConstants;
+import com.bs.common.core.domain.BaseEntity;
+
+/**
+ * 字典数据表 sys_dict_data
+ *
+ * @author bs
+ */
+@Data
+public class SysDictData extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 字典编码 */
+ @Excel(name = "字典编码", cellType = ColumnType.NUMERIC)
+ private Long dictCode;
+
+ /** 字典排序 */
+ @Excel(name = "字典排序", cellType = ColumnType.NUMERIC)
+ private Long dictSort;
+
+ /** 字典标签 */
+ @Excel(name = "字典标签")
+ @NotBlank(message = "字典标签不能为空")
+ @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
+ private String dictLabel;
+
+ /** 字典键值 */
+ @Excel(name = "字典键值")
+ @NotBlank(message = "字典键值不能为空")
+ @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
+ private String dictValue;
+
+ /** 字典类型 */
+ @Excel(name = "字典类型")
+ @NotBlank(message = "字典类型不能为空")
+ @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
+ private String dictType;
+
+ /** 样式属性(其他样式扩展) */
+ @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
+ private String cssClass;
+
+ /** 表格字典样式 */
+ private String listClass;
+
+ /** 是否默认(Y是 N否) */
+ @Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
+ private String isDefault;
+
+ /** 状态(0正常 1停用) */
+ @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+ private String status;
+ private String remark;
+
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDictType.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDictType.java
new file mode 100644
index 0000000..4e6664e
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysDictType.java
@@ -0,0 +1,98 @@
+package com.bs.common.core.domain.entity;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.bs.common.annotation.Excel;
+import com.bs.common.annotation.Excel.ColumnType;
+import com.bs.common.core.domain.BaseEntity;
+
+/**
+ * 字典类型表 sys_dict_type
+ *
+ * @author bs
+ */
+@Data
+public class SysDictType extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 字典主键 */
+ @Excel(name = "字典主键", cellType = ColumnType.NUMERIC)
+ private Long dictId;
+
+ /** 字典名称 */
+ @Excel(name = "字典名称")
+ private String dictName;
+
+ /** 字典类型 */
+ @Excel(name = "字典类型")
+ private String dictType;
+
+ /** 状态(0正常 1停用) */
+ @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+ private String status;
+ private String remark;
+ public Long getDictId()
+ {
+ return dictId;
+ }
+
+ public void setDictId(Long dictId)
+ {
+ this.dictId = dictId;
+ }
+
+ @NotBlank(message = "字典名称不能为空")
+ @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
+ public String getDictName()
+ {
+ return dictName;
+ }
+
+ public void setDictName(String dictName)
+ {
+ this.dictName = dictName;
+ }
+
+ @NotBlank(message = "字典类型不能为空")
+ @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
+ @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
+ public String getDictType()
+ {
+ return dictType;
+ }
+
+ public void setDictType(String dictType)
+ {
+ this.dictType = dictType;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("dictId", getDictId())
+ .append("dictName", getDictName())
+ .append("dictType", getDictType())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .toString();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysMenu.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysMenu.java
new file mode 100644
index 0000000..c0f8217
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysMenu.java
@@ -0,0 +1,268 @@
+package com.bs.common.core.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.bs.common.core.domain.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 菜单权限表 sys_menu
+ *
+ * @author bs
+ */
+@Data
+public class SysMenu extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 菜单ID */
+ @TableId(value = "menu_id", type = IdType.AUTO)
+ private Long menuId;
+
+ /** 菜单名称 */
+ private String menuName;
+
+ /** 父菜单名称 */
+ @TableField(exist = false)
+ private String parentName;
+
+ /** 父菜单ID */
+ private Long parentId;
+
+ /** 显示顺序 */
+ private Integer orderNum;
+
+ /** 路由地址 */
+ private String path;
+
+ /** 组件路径 */
+ private String component;
+
+ /** 路由参数 */
+ @TableField("`query`")
+ private String query;
+
+ /** 是否为外链(0是 1否) */
+ private String isFrame;
+
+ /** 是否缓存(0缓存 1不缓存) */
+ private String isCache;
+
+ /** 类型(M目录 C菜单 F按钮) */
+ private String menuType;
+
+ /** 显示状态(0显示 1隐藏) */
+ private String visible;
+
+ /** 菜单状态(0正常 1停用) */
+ private String status;
+
+ /** 权限字符串 */
+ private String perms;
+
+ /** 菜单图标 */
+ private String icon;
+ private String remark;
+ /** 子菜单 */
+ @TableField(exist = false)
+ private List children = new ArrayList();
+
+ public Long getMenuId()
+ {
+ return menuId;
+ }
+
+ public void setMenuId(Long menuId)
+ {
+ this.menuId = menuId;
+ }
+
+ @NotBlank(message = "菜单名称不能为空")
+ @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
+ public String getMenuName()
+ {
+ return menuName;
+ }
+
+ public void setMenuName(String menuName)
+ {
+ this.menuName = menuName;
+ }
+
+ public String getParentName()
+ {
+ return parentName;
+ }
+
+ public void setParentName(String parentName)
+ {
+ this.parentName = parentName;
+ }
+
+ public Long getParentId()
+ {
+ return parentId;
+ }
+
+ public void setParentId(Long parentId)
+ {
+ this.parentId = parentId;
+ }
+
+ @NotNull(message = "显示顺序不能为空")
+ public Integer getOrderNum()
+ {
+ return orderNum;
+ }
+
+ public void setOrderNum(Integer orderNum)
+ {
+ this.orderNum = orderNum;
+ }
+
+ @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
+ public String getPath()
+ {
+ return path;
+ }
+
+ public void setPath(String path)
+ {
+ this.path = path;
+ }
+
+ @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
+ public String getComponent()
+ {
+ return component;
+ }
+
+ public void setComponent(String component)
+ {
+ this.component = component;
+ }
+
+ public String getQuery()
+ {
+ return query;
+ }
+
+ public void setQuery(String query)
+ {
+ this.query = query;
+ }
+
+ public String getIsFrame()
+ {
+ return isFrame;
+ }
+
+ public void setIsFrame(String isFrame)
+ {
+ this.isFrame = isFrame;
+ }
+
+ public String getIsCache()
+ {
+ return isCache;
+ }
+
+ public void setIsCache(String isCache)
+ {
+ this.isCache = isCache;
+ }
+
+ @NotBlank(message = "菜单类型不能为空")
+ public String getMenuType()
+ {
+ return menuType;
+ }
+
+ public void setMenuType(String menuType)
+ {
+ this.menuType = menuType;
+ }
+
+ public String getVisible()
+ {
+ return visible;
+ }
+
+ public void setVisible(String visible)
+ {
+ this.visible = visible;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
+ public String getPerms()
+ {
+ return perms;
+ }
+
+ public void setPerms(String perms)
+ {
+ this.perms = perms;
+ }
+
+ public String getIcon()
+ {
+ return icon;
+ }
+
+ public void setIcon(String icon)
+ {
+ this.icon = icon;
+ }
+
+ public List getChildren()
+ {
+ return children;
+ }
+
+ public void setChildren(List children)
+ {
+ this.children = children;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("menuId", getMenuId())
+ .append("menuName", getMenuName())
+ .append("parentId", getParentId())
+ .append("orderNum", getOrderNum())
+ .append("path", getPath())
+ .append("component", getComponent())
+ .append("isFrame", getIsFrame())
+ .append("IsCache", getIsCache())
+ .append("menuType", getMenuType())
+ .append("visible", getVisible())
+ .append("status ", getStatus())
+ .append("perms", getPerms())
+ .append("icon", getIcon())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .toString();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysRole.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysRole.java
new file mode 100644
index 0000000..2ea7a45
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysRole.java
@@ -0,0 +1,239 @@
+package com.bs.common.core.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.bs.common.annotation.Excel;
+import com.bs.common.annotation.Excel.ColumnType;
+import com.bs.common.core.domain.BaseEntity;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.Set;
+
+/**
+ * 角色表 sys_role
+ *
+ * @author bs
+ */
+@Data
+public class SysRole extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 角色ID */
+ @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
+ @TableId(value = "role_id", type = IdType.AUTO)
+ private Long roleId;
+
+ /** 角色名称 */
+ @Excel(name = "角色名称")
+ private String roleName;
+
+ /** 角色权限 */
+ @Excel(name = "角色权限")
+ private String roleKey;
+
+ /** 角色排序 */
+ @Excel(name = "角色排序")
+ private Integer roleSort;
+
+ /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
+ @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
+ private String dataScope;
+ private String remark;
+ /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
+ private boolean menuCheckStrictly;
+
+ /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
+ private boolean deptCheckStrictly;
+
+ /** 角色状态(0正常 1停用) */
+ @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
+ private String status;
+
+ /** 用户是否存在此角色标识 默认不存在 */
+ @TableField(exist = false)
+ private boolean flag = false;
+
+ /** 菜单组 */
+ @TableField(exist = false)
+ private Long[] menuIds;
+
+ /** 部门组(数据权限) */
+ @TableField(exist = false)
+ private Long[] deptIds;
+
+ /** 角色菜单权限 */
+ @TableField(exist = false)
+ private Set permissions;
+
+ public SysRole()
+ {
+
+ }
+
+ public SysRole(Long roleId)
+ {
+ this.roleId = roleId;
+ }
+
+ public Long getRoleId()
+ {
+ return roleId;
+ }
+
+ public void setRoleId(Long roleId)
+ {
+ this.roleId = roleId;
+ }
+
+ public boolean isAdmin()
+ {
+ return isAdmin(this.roleId);
+ }
+
+ public static boolean isAdmin(Long roleId)
+ {
+ return roleId != null && 1L == roleId;
+ }
+
+ @NotBlank(message = "角色名称不能为空")
+ @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
+ public String getRoleName()
+ {
+ return roleName;
+ }
+
+ public void setRoleName(String roleName)
+ {
+ this.roleName = roleName;
+ }
+
+ @NotBlank(message = "权限字符不能为空")
+ @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
+ public String getRoleKey()
+ {
+ return roleKey;
+ }
+
+ public void setRoleKey(String roleKey)
+ {
+ this.roleKey = roleKey;
+ }
+
+ @NotNull(message = "显示顺序不能为空")
+ public Integer getRoleSort()
+ {
+ return roleSort;
+ }
+
+ public void setRoleSort(Integer roleSort)
+ {
+ this.roleSort = roleSort;
+ }
+
+ public String getDataScope()
+ {
+ return dataScope;
+ }
+
+ public void setDataScope(String dataScope)
+ {
+ this.dataScope = dataScope;
+ }
+
+ public boolean isMenuCheckStrictly()
+ {
+ return menuCheckStrictly;
+ }
+
+ public void setMenuCheckStrictly(boolean menuCheckStrictly)
+ {
+ this.menuCheckStrictly = menuCheckStrictly;
+ }
+
+ public boolean isDeptCheckStrictly()
+ {
+ return deptCheckStrictly;
+ }
+
+ public void setDeptCheckStrictly(boolean deptCheckStrictly)
+ {
+ this.deptCheckStrictly = deptCheckStrictly;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+
+ public boolean isFlag()
+ {
+ return flag;
+ }
+
+ public void setFlag(boolean flag)
+ {
+ this.flag = flag;
+ }
+
+ public Long[] getMenuIds()
+ {
+ return menuIds;
+ }
+
+ public void setMenuIds(Long[] menuIds)
+ {
+ this.menuIds = menuIds;
+ }
+
+ public Long[] getDeptIds()
+ {
+ return deptIds;
+ }
+
+ public void setDeptIds(Long[] deptIds)
+ {
+ this.deptIds = deptIds;
+ }
+
+ public Set getPermissions()
+ {
+ return permissions;
+ }
+
+ public void setPermissions(Set permissions)
+ {
+ this.permissions = permissions;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("roleId", getRoleId())
+ .append("roleName", getRoleName())
+ .append("roleKey", getRoleKey())
+ .append("roleSort", getRoleSort())
+ .append("dataScope", getDataScope())
+ .append("menuCheckStrictly", isMenuCheckStrictly())
+ .append("deptCheckStrictly", isDeptCheckStrictly())
+ .append("status", getStatus())
+ .append("delFlag", getDelFlag())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .toString();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysTenant.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysTenant.java
new file mode 100644
index 0000000..08c8b33
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysTenant.java
@@ -0,0 +1,57 @@
+
+package com.bs.common.core.domain.entity;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.bs.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class SysTenant extends BaseEntity {
+
+ @TableId(value = "ID", type = IdType.AUTO)
+ private Long id;
+ private String name;
+ private String simpleName;
+ private String contactUser;
+ private String contactPhone;
+ private Integer status;
+ private String userName;
+ private String password;
+
+ @ApiModelProperty(value = "行政区划")
+ private String areaCode;
+ /**
+ * 所有模块集合
+ */
+ @TableField(exist = false)
+ private String systemNames;
+ @TableField(exist = false)
+ private Long tenantId;
+ @TableField(exist = false)
+ private String deptId;
+ @TableField(exist = false)
+ private TenantConfig tenantConfig;
+ /**
+ * 备注
+ */
+ private String remark;
+ /**
+ * 所属系统
+ */
+ @TableField(exist = false)
+ private List systems;
+
+ @TableField(exist = false)
+ private String areaName;
+
+ @TableField(exist = false)
+ private List roleIds;
+
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/SysUser.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysUser.java
new file mode 100644
index 0000000..109a319
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/SysUser.java
@@ -0,0 +1,142 @@
+package com.bs.common.core.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.bs.common.annotation.Excel;
+import com.bs.common.annotation.Excel.ColumnType;
+import com.bs.common.annotation.Excel.Type;
+import com.bs.common.annotation.Excels;
+import com.bs.common.core.domain.BaseEntity;
+import com.bs.common.xss.Xss;
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 用户对象 sys_user
+ *
+ * @author bs
+ */
+@Data
+public class SysUser extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 用户ID */
+ @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
+ @TableId(value = "user_id",type = IdType.AUTO)
+ private Long userId;
+
+ /** 部门ID */
+ @Excel(name = "部门编号", type = Type.IMPORT)
+ private Long deptId;
+
+ /** 用户账号 */
+ @Excel(name = "登录名称")
+ @Xss(message = "用户账号不能包含脚本字符")
+ @NotBlank(message = "用户账号不能为空")
+ @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
+ private String userName;
+
+ /** 用户昵称 */
+ @Excel(name = "用户名称")
+ @Xss(message = "用户昵称不能包含脚本字符")
+ @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
+ private String nickName;
+
+ /** 用户邮箱 */
+ @Excel(name = "用户邮箱")
+ @Email(message = "邮箱格式不正确")
+ @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+ private String email;
+
+ /** 手机号码 */
+ @Excel(name = "手机号码")
+ @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
+ private String phonenumber;
+
+ /** 用户性别 */
+ @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
+ private String sex;
+
+ /** 用户头像 */
+ private String avatar;
+
+ /** 密码 */
+ private String password;
+
+ /** 帐号状态(0正常 1停用) */
+ @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
+ private String status;
+
+ /** 删除标志(0代表存在 2代表删除) */
+ private String delFlag;
+
+ /** 最后登录IP */
+ @Excel(name = "最后登录IP", type = Type.EXPORT)
+ private String loginIp;
+
+
+ /** 是否是租户,Y是N,不是 */
+ @Excel(name = "是否是租户,Y是N,不是", type = Type.EXPORT)
+ private String isTenant;
+
+ /** 是否是租户管理员,Y是N,不是 */
+ @Excel(name = "是否是租户管理员,Y是N,不是", type = Type.EXPORT)
+ private String isTenantAdmin;
+
+ /** 最后登录时间 */
+ @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
+ private Date loginDate;
+
+ /** 部门对象 */
+ @Excels({
+ @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
+ @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
+ })
+ @TableField(exist = false)
+ private SysDept dept;
+
+ /** 角色对象 */
+ @TableField(exist = false)
+ private List roles;
+
+ /** 角色组 */
+ @TableField(exist = false)
+ private Long[] roleIds;
+
+ /** 岗位组 */
+ @TableField(exist = false)
+ private Long[] postIds;
+
+ /** 角色ID */
+ @TableField(exist = false)
+ private Long roleId;
+ private String remark;
+ public SysUser()
+ {
+
+ }
+
+ public SysUser(Long userId)
+ {
+ this.userId = userId;
+ }
+
+ public boolean isAdmin()
+ {
+ return isAdmin(this.userId);
+ }
+
+ public static boolean isAdmin(Long userId)
+ {
+ return userId != null && 1L == userId;
+ }
+
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/entity/TenantConfig.java b/bs-common/src/main/java/com/bs/common/core/domain/entity/TenantConfig.java
new file mode 100644
index 0000000..d091d14
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/entity/TenantConfig.java
@@ -0,0 +1,156 @@
+package com.bs.common.core.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.bs.common.annotation.Excel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 租户配置对象 tenant_config
+ *
+ * @author ruoyi
+ * @date 2023-04-02
+ */
+@Accessors(chain = true)
+@TableName("tenant_config")
+@Data
+public class TenantConfig
+{
+ private static final long serialVersionUID = 1L;
+
+ /** $column.columnComment */
+ @TableId(value = "ID",type = IdType.AUTO)
+ @ApiModelProperty(value = "${column.columnComment}")
+ private Long id;
+
+ /**
+ * 创建部门
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Long createDept;
+ @TableField(exist = false)
+ private Long tenantId;
+
+ /**
+ * 创建者
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private Long createBy;
+
+ /**
+ * 创建时间
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ /**
+ * 更新者
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updateBy;
+
+ /**
+ * 更新时间
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+ /**
+ * 逻辑删除标示
+ */
+ //做删除操作的时候,会变成update,del_flag = '2'
+ @TableLogic(value = "0", delval = "2")
+ @TableField(fill = FieldFill.INSERT)
+ @JsonIgnore
+ private String delFlag;
+
+
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
+ @ApiModelProperty(value = "开始时间")
+ private Date startData;
+
+ /** 结束时间 */
+
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd")
+ @ApiModelProperty(value = "结束时间")
+ private Date stopData;
+
+ /** 是否限制用户数 */
+
+ @Excel(name = "是否限制用户数")
+ @ApiModelProperty(value = "是否限制用户数")
+ private String isUserAstrict;
+
+ @Excel(name = "首页类型")
+ @ApiModelProperty(value = "首页类型")
+ private String homeType;
+
+ /** 用户数 */
+
+ @Excel(name = "用户数")
+ @ApiModelProperty(value = "用户数")
+ private Integer userQuantity;
+
+ /** 是否限制在线数 */
+
+ @Excel(name = "是否限制在线数")
+ @ApiModelProperty(value = "是否限制在线数")
+ private String isOnLine;
+
+ /** 在线数 */
+
+ @Excel(name = "在线数")
+ @ApiModelProperty(value = "在线数")
+ private Integer onLineUserQuantity;
+
+ /** 是否定义系统名称 */
+
+ @Excel(name = "是否定义系统名称")
+ @ApiModelProperty(value = "是否定义系统名称")
+ private String isDefinitionName;
+
+ /** 系统名称 */
+
+ @Excel(name = "系统名称")
+ @ApiModelProperty(value = "系统名称")
+ private String sysName;
+
+ /** 是否定义系统logo */
+
+ @Excel(name = "是否定义系统logo")
+ @ApiModelProperty(value = "是否定义系统logo")
+ private String isDefinitionLogo;
+
+ /** 系统log */
+
+ @Excel(name = "系统log")
+ @ApiModelProperty(value = "系统log")
+ private String sysLog;
+
+ /** 是否定义系统色调 */
+
+ @Excel(name = "是否定义系统色调")
+ @ApiModelProperty(value = "是否定义系统色调")
+ private String isDefinitionHue;
+
+ /** 系统色调 */
+
+ @Excel(name = "系统色调")
+ @ApiModelProperty(value = "系统色调")
+ private String sysHue;
+
+ /** 定制化数据,键值对{key:{type1:"",type2:"",value:""},key2:{type1:"",type2:"",value:""}}具体操作时候,做具体说明 */
+
+ @ApiModelProperty(value = "定制化数据,键值对{key:{type1:'',type2:'',value:''},key2:{type1:'',type2:'',value:''}}具体操作时候,做具体说明")
+ private String customizationFields;
+
+
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/model/LoginBody.java b/bs-common/src/main/java/com/bs/common/core/domain/model/LoginBody.java
new file mode 100644
index 0000000..a61a351
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/model/LoginBody.java
@@ -0,0 +1,69 @@
+package com.bs.common.core.domain.model;
+
+/**
+ * 用户登录对象
+ *
+ * @author bs
+ */
+public class LoginBody
+{
+ /**
+ * 用户名
+ */
+ private String username;
+
+ /**
+ * 用户密码
+ */
+ private String password;
+
+ /**
+ * 验证码
+ */
+ private String code;
+
+ /**
+ * 唯一标识
+ */
+ private String uuid;
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public void setUsername(String username)
+ {
+ this.username = username;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public void setCode(String code)
+ {
+ this.code = code;
+ }
+
+ public String getUuid()
+ {
+ return uuid;
+ }
+
+ public void setUuid(String uuid)
+ {
+ this.uuid = uuid;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/model/LoginUser.java b/bs-common/src/main/java/com/bs/common/core/domain/model/LoginUser.java
new file mode 100644
index 0000000..096e578
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/model/LoginUser.java
@@ -0,0 +1,312 @@
+package com.bs.common.core.domain.model;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.bs.common.core.domain.entity.SysUser;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * 登录用户身份权限
+ *
+ * @author bs
+ */
+public class LoginUser implements UserDetails
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 部门ID
+ */
+ private Long deptId;
+
+ /**
+ * 用户唯一标识
+ */
+ private String token;
+
+ /**
+ * 登录时间
+ */
+ private Long loginTime;
+
+ /**
+ * 过期时间
+ */
+ private Long expireTime;
+
+ /**
+ * 登录IP地址
+ */
+ private String ipaddr;
+
+ /**
+ * 登录地点
+ */
+ private String loginLocation;
+
+ /**
+ * 浏览器类型
+ */
+ private String browser;
+
+ /**
+ * 操作系统
+ */
+ private String os;
+
+ /**
+ * 权限列表
+ */
+ private Set permissions;
+
+ /**
+ * 用户信息
+ */
+ private SysUser user;
+
+ /**
+ * 租户id
+ */
+ private Long tenantId;
+ /**
+ * 是否租户
+ */
+ private String isTenant;
+ /** 是否租户管理员 */
+ /**
+ * 是否租户admin
+ * Y 是 N 否
+ *
+ * @return
+ */
+ private String isTenantAdmin;
+
+ public Long getTenantId()
+ {
+ return tenantId;
+ }
+
+ public void setTenantId(Long tenantId)
+ {
+ this.tenantId = tenantId;
+ }
+
+ public String getIsTenant() {
+ return isTenant;
+ }
+
+ public void setIsTenant(String isTenant) {
+ this.isTenant = isTenant;
+ }
+
+ public String getIsTenantAdmin() {
+ return isTenantAdmin;
+ }
+
+ public void setIsTenantAdmin(String isTenantAdmin) {
+ this.isTenantAdmin = isTenantAdmin;
+ }
+
+ public LoginUser()
+ {
+ }
+
+ public LoginUser(SysUser user, Set permissions)
+ {
+ this.user = user;
+ this.permissions = permissions;
+ }
+
+ public LoginUser(Long userId, Long deptId, SysUser user, Set permissions)
+ {
+ this.userId = userId;
+ this.deptId = deptId;
+ this.tenantId = user.getTenantId();
+ this.isTenant = user.getIsTenant();
+ this.isTenantAdmin = user.getIsTenantAdmin();
+ this.user = user;
+ this.permissions = permissions;
+ }
+
+ public Long getUserId()
+ {
+ return userId;
+ }
+
+ public void setUserId(Long userId)
+ {
+ this.userId = userId;
+ }
+
+ public Long getDeptId()
+ {
+ return deptId;
+ }
+
+ public void setDeptId(Long deptId)
+ {
+ this.deptId = deptId;
+ }
+
+ public String getToken()
+ {
+ return token;
+ }
+
+ public void setToken(String token)
+ {
+ this.token = token;
+ }
+
+ @JSONField(serialize = false)
+ @Override
+ public String getPassword()
+ {
+ return user.getPassword();
+ }
+
+ @Override
+ public String getUsername()
+ {
+ return user.getUserName();
+ }
+
+ /**
+ * 账户是否未过期,过期无法验证
+ */
+ @JSONField(serialize = false)
+ @Override
+ public boolean isAccountNonExpired()
+ {
+ return true;
+ }
+
+ /**
+ * 指定用户是否解锁,锁定的用户无法进行身份验证
+ *
+ * @return
+ */
+ @JSONField(serialize = false)
+ @Override
+ public boolean isAccountNonLocked()
+ {
+ return true;
+ }
+
+ /**
+ * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
+ *
+ * @return
+ */
+ @JSONField(serialize = false)
+ @Override
+ public boolean isCredentialsNonExpired()
+ {
+ return true;
+ }
+
+ /**
+ * 是否可用 ,禁用的用户不能身份验证
+ *
+ * @return
+ */
+ @JSONField(serialize = false)
+ @Override
+ public boolean isEnabled()
+ {
+ return true;
+ }
+
+ public Long getLoginTime()
+ {
+ return loginTime;
+ }
+
+ public void setLoginTime(Long loginTime)
+ {
+ this.loginTime = loginTime;
+ }
+
+ public String getIpaddr()
+ {
+ return ipaddr;
+ }
+
+ public void setIpaddr(String ipaddr)
+ {
+ this.ipaddr = ipaddr;
+ }
+
+ public String getLoginLocation()
+ {
+ return loginLocation;
+ }
+
+ public void setLoginLocation(String loginLocation)
+ {
+ this.loginLocation = loginLocation;
+ }
+
+ public String getBrowser()
+ {
+ return browser;
+ }
+
+ public void setBrowser(String browser)
+ {
+ this.browser = browser;
+ }
+
+ public String getOs()
+ {
+ return os;
+ }
+
+ public void setOs(String os)
+ {
+ this.os = os;
+ }
+
+ public Long getExpireTime()
+ {
+ return expireTime;
+ }
+
+ public void setExpireTime(Long expireTime)
+ {
+ this.expireTime = expireTime;
+ }
+
+ public Set getPermissions()
+ {
+ return permissions;
+ }
+
+ public void setPermissions(Set permissions)
+ {
+ this.permissions = permissions;
+ }
+
+ public SysUser getUser()
+ {
+ return user;
+ }
+
+ public void setUser(SysUser user)
+ {
+ this.user = user;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities()
+ {
+ return null;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/domain/model/RegisterBody.java b/bs-common/src/main/java/com/bs/common/core/domain/model/RegisterBody.java
new file mode 100644
index 0000000..dba2c9f
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/domain/model/RegisterBody.java
@@ -0,0 +1,11 @@
+package com.bs.common.core.domain.model;
+
+/**
+ * 用户注册对象
+ *
+ * @author bs
+ */
+public class RegisterBody extends LoginBody
+{
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/page/PageDomain.java b/bs-common/src/main/java/com/bs/common/core/page/PageDomain.java
new file mode 100644
index 0000000..9a8cb43
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/page/PageDomain.java
@@ -0,0 +1,101 @@
+package com.bs.common.core.page;
+
+import com.bs.common.utils.StringUtils;
+
+/**
+ * 分页数据
+ *
+ * @author bs
+ */
+public class PageDomain
+{
+ /** 当前记录起始索引 */
+ private Integer pageNum;
+
+ /** 每页显示记录数 */
+ private Integer pageSize;
+
+ /** 排序列 */
+ private String orderByColumn;
+
+ /** 排序的方向desc或者asc */
+ private String isAsc = "asc";
+
+ /** 分页参数合理化 */
+ private Boolean reasonable = true;
+
+ public String getOrderBy()
+ {
+ if (StringUtils.isEmpty(orderByColumn))
+ {
+ return "";
+ }
+ return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
+ }
+
+ public Integer getPageNum()
+ {
+ return pageNum;
+ }
+
+ public void setPageNum(Integer pageNum)
+ {
+ this.pageNum = pageNum;
+ }
+
+ public Integer getPageSize()
+ {
+ return pageSize;
+ }
+
+ public void setPageSize(Integer pageSize)
+ {
+ this.pageSize = pageSize;
+ }
+
+ public String getOrderByColumn()
+ {
+ return orderByColumn;
+ }
+
+ public void setOrderByColumn(String orderByColumn)
+ {
+ this.orderByColumn = orderByColumn;
+ }
+
+ public String getIsAsc()
+ {
+ return isAsc;
+ }
+
+ public void setIsAsc(String isAsc)
+ {
+ if (StringUtils.isNotEmpty(isAsc))
+ {
+ // 兼容前端排序类型
+ if ("ascending".equals(isAsc))
+ {
+ isAsc = "asc";
+ }
+ else if ("descending".equals(isAsc))
+ {
+ isAsc = "desc";
+ }
+ this.isAsc = isAsc;
+ }
+ }
+
+ public Boolean getReasonable()
+ {
+ if (StringUtils.isNull(reasonable))
+ {
+ return Boolean.TRUE;
+ }
+ return reasonable;
+ }
+
+ public void setReasonable(Boolean reasonable)
+ {
+ this.reasonable = reasonable;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/page/TableDataInfo.java b/bs-common/src/main/java/com/bs/common/core/page/TableDataInfo.java
new file mode 100644
index 0000000..9f96f91
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/page/TableDataInfo.java
@@ -0,0 +1,85 @@
+package com.bs.common.core.page;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 表格分页数据对象
+ *
+ * @author bs
+ */
+public class TableDataInfo implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 总记录数 */
+ private long total;
+
+ /** 列表数据 */
+ private List> rows;
+
+ /** 消息状态码 */
+ private int code;
+
+ /** 消息内容 */
+ private String msg;
+
+ /**
+ * 表格数据对象
+ */
+ public TableDataInfo()
+ {
+ }
+
+ /**
+ * 分页
+ *
+ * @param list 列表数据
+ * @param total 总记录数
+ */
+ public TableDataInfo(List> list, int total)
+ {
+ this.rows = list;
+ this.total = total;
+ }
+
+ public long getTotal()
+ {
+ return total;
+ }
+
+ public void setTotal(long total)
+ {
+ this.total = total;
+ }
+
+ public List> getRows()
+ {
+ return rows;
+ }
+
+ public void setRows(List> rows)
+ {
+ this.rows = rows;
+ }
+
+ public int getCode()
+ {
+ return code;
+ }
+
+ public void setCode(int code)
+ {
+ this.code = code;
+ }
+
+ public String getMsg()
+ {
+ return msg;
+ }
+
+ public void setMsg(String msg)
+ {
+ this.msg = msg;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/page/TableSupport.java b/bs-common/src/main/java/com/bs/common/core/page/TableSupport.java
new file mode 100644
index 0000000..ba7c34c
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/page/TableSupport.java
@@ -0,0 +1,56 @@
+package com.bs.common.core.page;
+
+import com.bs.common.core.text.Convert;
+import com.bs.common.utils.ServletUtils;
+
+/**
+ * 表格数据处理
+ *
+ * @author bs
+ */
+public class TableSupport
+{
+ /**
+ * 当前记录起始索引
+ */
+ 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 REASONABLE = "reasonable";
+
+ /**
+ * 封装分页对象
+ */
+ public static PageDomain getPageDomain()
+ {
+ PageDomain pageDomain = new PageDomain();
+ pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
+ pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
+ pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
+ pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
+ pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
+ return pageDomain;
+ }
+
+ public static PageDomain buildPageRequest()
+ {
+ return getPageDomain();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/redis/RedisCache.java b/bs-common/src/main/java/com/bs/common/core/redis/RedisCache.java
new file mode 100644
index 0000000..d83dbe9
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/redis/RedisCache.java
@@ -0,0 +1,268 @@
+package com.bs.common.core.redis;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring redis 工具类
+ *
+ * @author bs
+ **/
+@SuppressWarnings(value = { "unchecked", "rawtypes" })
+@Component
+public class RedisCache
+{
+ @Autowired
+ public RedisTemplate redisTemplate;
+
+ /**
+ * 缓存基本的对象,Integer、String、实体类等
+ *
+ * @param key 缓存的键值
+ * @param value 缓存的值
+ */
+ public void setCacheObject(final String key, final T value)
+ {
+ redisTemplate.opsForValue().set(key, value);
+ }
+
+ /**
+ * 缓存基本的对象,Integer、String、实体类等
+ *
+ * @param key 缓存的键值
+ * @param value 缓存的值
+ * @param timeout 时间
+ * @param timeUnit 时间颗粒度
+ */
+ public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
+ {
+ redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
+ }
+
+ /**
+ * 设置有效时间
+ *
+ * @param key Redis键
+ * @param timeout 超时时间
+ * @return true=设置成功;false=设置失败
+ */
+ public boolean expire(final String key, final long timeout)
+ {
+ return expire(key, timeout, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 设置有效时间
+ *
+ * @param key Redis键
+ * @param timeout 超时时间
+ * @param unit 时间单位
+ * @return true=设置成功;false=设置失败
+ */
+ public boolean expire(final String key, final long timeout, final TimeUnit unit)
+ {
+ return redisTemplate.expire(key, timeout, unit);
+ }
+
+ /**
+ * 获取有效时间
+ *
+ * @param key Redis键
+ * @return 有效时间
+ */
+ public long getExpire(final String key)
+ {
+ return redisTemplate.getExpire(key);
+ }
+
+ /**
+ * 判断 key是否存在
+ *
+ * @param key 键
+ * @return true 存在 false不存在
+ */
+ public Boolean hasKey(String key)
+ {
+ return redisTemplate.hasKey(key);
+ }
+
+ /**
+ * 获得缓存的基本对象。
+ *
+ * @param key 缓存键值
+ * @return 缓存键值对应的数据
+ */
+ public T getCacheObject(final String key)
+ {
+ ValueOperations operation = redisTemplate.opsForValue();
+ return operation.get(key);
+ }
+
+ /**
+ * 删除单个对象
+ *
+ * @param key
+ */
+ public boolean deleteObject(final String key)
+ {
+ return redisTemplate.delete(key);
+ }
+
+ /**
+ * 删除集合对象
+ *
+ * @param collection 多个对象
+ * @return
+ */
+ public boolean deleteObject(final Collection collection)
+ {
+ return redisTemplate.delete(collection) > 0;
+ }
+
+ /**
+ * 缓存List数据
+ *
+ * @param key 缓存的键值
+ * @param dataList 待缓存的List数据
+ * @return 缓存的对象
+ */
+ public long setCacheList(final String key, final List dataList)
+ {
+ Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+ return count == null ? 0 : count;
+ }
+
+ /**
+ * 获得缓存的list对象
+ *
+ * @param key 缓存的键值
+ * @return 缓存键值对应的数据
+ */
+ public List getCacheList(final String key)
+ {
+ return redisTemplate.opsForList().range(key, 0, -1);
+ }
+
+ /**
+ * 缓存Set
+ *
+ * @param key 缓存键值
+ * @param dataSet 缓存的数据
+ * @return 缓存数据的对象
+ */
+ public BoundSetOperations setCacheSet(final String key, final Set dataSet)
+ {
+ BoundSetOperations setOperation = redisTemplate.boundSetOps(key);
+ Iterator it = dataSet.iterator();
+ while (it.hasNext())
+ {
+ setOperation.add(it.next());
+ }
+ return setOperation;
+ }
+
+ /**
+ * 获得缓存的set
+ *
+ * @param key
+ * @return
+ */
+ public Set getCacheSet(final String key)
+ {
+ return redisTemplate.opsForSet().members(key);
+ }
+
+ /**
+ * 缓存Map
+ *
+ * @param key
+ * @param dataMap
+ */
+ public void setCacheMap(final String key, final Map dataMap)
+ {
+ if (dataMap != null) {
+ redisTemplate.opsForHash().putAll(key, dataMap);
+ }
+ }
+
+ /**
+ * 获得缓存的Map
+ *
+ * @param key
+ * @return
+ */
+ public Map getCacheMap(final String key)
+ {
+ return redisTemplate.opsForHash().entries(key);
+ }
+
+ /**
+ * 往Hash中存入数据
+ *
+ * @param key Redis键
+ * @param hKey Hash键
+ * @param value 值
+ */
+ public void setCacheMapValue(final String key, final String hKey, final T value)
+ {
+ redisTemplate.opsForHash().put(key, hKey, value);
+ }
+
+ /**
+ * 获取Hash中的数据
+ *
+ * @param key Redis键
+ * @param hKey Hash键
+ * @return Hash中的对象
+ */
+ public T getCacheMapValue(final String key, final String hKey)
+ {
+ HashOperations opsForHash = redisTemplate.opsForHash();
+ return opsForHash.get(key, hKey);
+ }
+
+ /**
+ * 获取多个Hash中的数据
+ *
+ * @param key Redis键
+ * @param hKeys Hash键集合
+ * @return Hash对象集合
+ */
+ public List getMultiCacheMapValue(final String key, final Collection hKeys)
+ {
+ return redisTemplate.opsForHash().multiGet(key, hKeys);
+ }
+
+ /**
+ * 删除Hash中的某条数据
+ *
+ * @param key Redis键
+ * @param hKey Hash键
+ * @return 是否成功
+ */
+ public boolean deleteCacheMapValue(final String key, final String hKey)
+ {
+ return redisTemplate.opsForHash().delete(key, hKey) > 0;
+ }
+
+ /**
+ * 获得缓存的基本对象列表
+ *
+ * @param pattern 字符串前缀
+ * @return 对象列表
+ */
+ public Collection keys(final String pattern)
+ {
+ return redisTemplate.keys(pattern);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/text/CharsetKit.java b/bs-common/src/main/java/com/bs/common/core/text/CharsetKit.java
new file mode 100644
index 0000000..ef506dd
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/text/CharsetKit.java
@@ -0,0 +1,86 @@
+package com.bs.common.core.text;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import com.bs.common.utils.StringUtils;
+
+/**
+ * 字符集工具类
+ *
+ * @author bs
+ */
+public class CharsetKit
+{
+ /** ISO-8859-1 */
+ public static final String ISO_8859_1 = "ISO-8859-1";
+ /** UTF-8 */
+ public static final String UTF_8 = "UTF-8";
+ /** GBK */
+ public static final String GBK = "GBK";
+
+ /** ISO-8859-1 */
+ public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+ /** UTF-8 */
+ public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+ /** GBK */
+ public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+ /**
+ * 转换为Charset对象
+ *
+ * @param charset 字符集,为空则返回默认字符集
+ * @return Charset
+ */
+ public static Charset charset(String charset)
+ {
+ return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * @param source 字符串
+ * @param srcCharset 源字符集,默认ISO-8859-1
+ * @param destCharset 目标字符集,默认UTF-8
+ * @return 转换后的字符集
+ */
+ public static String convert(String source, String srcCharset, String destCharset)
+ {
+ return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * @param source 字符串
+ * @param srcCharset 源字符集,默认ISO-8859-1
+ * @param destCharset 目标字符集,默认UTF-8
+ * @return 转换后的字符集
+ */
+ public static String convert(String source, Charset srcCharset, Charset destCharset)
+ {
+ if (null == srcCharset)
+ {
+ srcCharset = StandardCharsets.ISO_8859_1;
+ }
+
+ if (null == destCharset)
+ {
+ destCharset = StandardCharsets.UTF_8;
+ }
+
+ if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+ {
+ return source;
+ }
+ return new String(source.getBytes(srcCharset), destCharset);
+ }
+
+ /**
+ * @return 系统字符集编码
+ */
+ public static String systemCharset()
+ {
+ return Charset.defaultCharset().name();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/text/Convert.java b/bs-common/src/main/java/com/bs/common/core/text/Convert.java
new file mode 100644
index 0000000..e6cb7cf
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/text/Convert.java
@@ -0,0 +1,1000 @@
+package com.bs.common.core.text;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+import com.bs.common.utils.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * 类型转换器
+ *
+ * @author bs
+ */
+public class Convert
+{
+ /**
+ * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static String toStr(Object value, String defaultValue)
+ {
+ if (null == value)
+ {
+ return defaultValue;
+ }
+ if (value instanceof String)
+ {
+ return (String) value;
+ }
+ return value.toString();
+ }
+
+ /**
+ * 转换为字符串
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static String toStr(Object value)
+ {
+ return toStr(value, null);
+ }
+
+ /**
+ * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Character toChar(Object value, Character defaultValue)
+ {
+ if (null == value)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Character)
+ {
+ return (Character) value;
+ }
+
+ final String valueStr = toStr(value, null);
+ return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+ }
+
+ /**
+ * 转换为字符
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Character toChar(Object value)
+ {
+ return toChar(value, null);
+ }
+
+ /**
+ * 转换为byte
+ * 如果给定的值为null
,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Byte toByte(Object value, Byte defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Byte)
+ {
+ return (Byte) value;
+ }
+ if (value instanceof Number)
+ {
+ return ((Number) value).byteValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return Byte.parseByte(valueStr);
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为byte
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Byte toByte(Object value)
+ {
+ return toByte(value, null);
+ }
+
+ /**
+ * 转换为Short
+ * 如果给定的值为null
,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Short toShort(Object value, Short defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Short)
+ {
+ return (Short) value;
+ }
+ if (value instanceof Number)
+ {
+ return ((Number) value).shortValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return Short.parseShort(valueStr.trim());
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Short
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Short toShort(Object value)
+ {
+ return toShort(value, null);
+ }
+
+ /**
+ * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Number toNumber(Object value, Number defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Number)
+ {
+ return (Number) value;
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return NumberFormat.getInstance().parse(valueStr);
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Number toNumber(Object value)
+ {
+ return toNumber(value, null);
+ }
+
+ /**
+ * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Integer toInt(Object value, Integer defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Integer)
+ {
+ return (Integer) value;
+ }
+ if (value instanceof Number)
+ {
+ return ((Number) value).intValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return Integer.parseInt(valueStr.trim());
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为int
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Integer toInt(Object value)
+ {
+ return toInt(value, null);
+ }
+
+ /**
+ * 转换为Integer数组
+ *
+ * @param str 被转换的值
+ * @return 结果
+ */
+ public static Integer[] toIntArray(String str)
+ {
+ return toIntArray(",", str);
+ }
+
+ /**
+ * 转换为Long数组
+ *
+ * @param str 被转换的值
+ * @return 结果
+ */
+ public static Long[] toLongArray(String str)
+ {
+ return toLongArray(",", str);
+ }
+
+ /**
+ * 转换为Integer数组
+ *
+ * @param split 分隔符
+ * @param split 被转换的值
+ * @return 结果
+ */
+ public static Integer[] toIntArray(String split, String str)
+ {
+ if (StringUtils.isEmpty(str))
+ {
+ return new Integer[] {};
+ }
+ String[] arr = str.split(split);
+ final Integer[] ints = new Integer[arr.length];
+ for (int i = 0; i < arr.length; i++)
+ {
+ final Integer v = toInt(arr[i], 0);
+ ints[i] = v;
+ }
+ return ints;
+ }
+
+ /**
+ * 转换为Long数组
+ *
+ * @param split 分隔符
+ * @param str 被转换的值
+ * @return 结果
+ */
+ public static Long[] toLongArray(String split, String str)
+ {
+ if (StringUtils.isEmpty(str))
+ {
+ return new Long[] {};
+ }
+ String[] arr = str.split(split);
+ final Long[] longs = new Long[arr.length];
+ for (int i = 0; i < arr.length; i++)
+ {
+ final Long v = toLong(arr[i], null);
+ longs[i] = v;
+ }
+ return longs;
+ }
+
+ /**
+ * 转换为String数组
+ *
+ * @param str 被转换的值
+ * @return 结果
+ */
+ public static String[] toStrArray(String str)
+ {
+ return toStrArray(",", str);
+ }
+
+ /**
+ * 转换为String数组
+ *
+ * @param split 分隔符
+ * @param split 被转换的值
+ * @return 结果
+ */
+ public static String[] toStrArray(String split, String str)
+ {
+ return str.split(split);
+ }
+
+ /**
+ * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Long toLong(Object value, Long defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Long)
+ {
+ return (Long) value;
+ }
+ if (value instanceof Number)
+ {
+ return ((Number) value).longValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ // 支持科学计数法
+ return new BigDecimal(valueStr.trim()).longValue();
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为long
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Long toLong(Object value)
+ {
+ return toLong(value, null);
+ }
+
+ /**
+ * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Double toDouble(Object value, Double defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Double)
+ {
+ return (Double) value;
+ }
+ if (value instanceof Number)
+ {
+ return ((Number) value).doubleValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ // 支持科学计数法
+ return new BigDecimal(valueStr.trim()).doubleValue();
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Double toDouble(Object value)
+ {
+ return toDouble(value, null);
+ }
+
+ /**
+ * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Float toFloat(Object value, Float defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Float)
+ {
+ return (Float) value;
+ }
+ if (value instanceof Number)
+ {
+ return ((Number) value).floatValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return Float.parseFloat(valueStr.trim());
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Float toFloat(Object value)
+ {
+ return toFloat(value, null);
+ }
+
+ /**
+ * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static Boolean toBool(Object value, Boolean defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof Boolean)
+ {
+ return (Boolean) value;
+ }
+ String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ valueStr = valueStr.trim().toLowerCase();
+ switch (valueStr)
+ {
+ case "true":
+ case "yes":
+ case "ok":
+ case "1":
+ return true;
+ case "false":
+ case "no":
+ case "0":
+ return false;
+ default:
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static Boolean toBool(Object value)
+ {
+ return toBool(value, null);
+ }
+
+ /**
+ * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ *
+ * @param clazz Enum的Class
+ * @param value 值
+ * @param defaultValue 默认值
+ * @return Enum
+ */
+ public static > E toEnum(Class clazz, Object value, E defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (clazz.isAssignableFrom(value.getClass()))
+ {
+ @SuppressWarnings("unchecked")
+ E myE = (E) value;
+ return myE;
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return Enum.valueOf(clazz, valueStr);
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ *
+ * @param clazz Enum的Class
+ * @param value 值
+ * @return Enum
+ */
+ public static > E toEnum(Class clazz, Object value)
+ {
+ return toEnum(clazz, value, null);
+ }
+
+ /**
+ * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof BigInteger)
+ {
+ return (BigInteger) value;
+ }
+ if (value instanceof Long)
+ {
+ return BigInteger.valueOf((Long) value);
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return new BigInteger(valueStr);
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static BigInteger toBigInteger(Object value)
+ {
+ return toBigInteger(value, null);
+ }
+
+ /**
+ * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ * @return 结果
+ */
+ public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
+ {
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ if (value instanceof BigDecimal)
+ {
+ return (BigDecimal) value;
+ }
+ if (value instanceof Long)
+ {
+ return new BigDecimal((Long) value);
+ }
+ if (value instanceof Double)
+ {
+ return BigDecimal.valueOf((Double) value);
+ }
+ if (value instanceof Integer)
+ {
+ return new BigDecimal((Integer) value);
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr))
+ {
+ return defaultValue;
+ }
+ try
+ {
+ return new BigDecimal(valueStr);
+ }
+ catch (Exception e)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @return 结果
+ */
+ public static BigDecimal toBigDecimal(Object value)
+ {
+ return toBigDecimal(value, null);
+ }
+
+ /**
+ * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+ *
+ * @param obj 对象
+ * @return 字符串
+ */
+ public static String utf8Str(Object obj)
+ {
+ return str(obj, CharsetKit.CHARSET_UTF_8);
+ }
+
+ /**
+ * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+ *
+ * @param obj 对象
+ * @param charsetName 字符集
+ * @return 字符串
+ */
+ public static String str(Object obj, String charsetName)
+ {
+ return str(obj, Charset.forName(charsetName));
+ }
+
+ /**
+ * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+ *
+ * @param obj 对象
+ * @param charset 字符集
+ * @return 字符串
+ */
+ public static String str(Object obj, Charset charset)
+ {
+ if (null == obj)
+ {
+ return null;
+ }
+
+ if (obj instanceof String)
+ {
+ return (String) obj;
+ }
+ else if (obj instanceof byte[])
+ {
+ return str((byte[]) obj, charset);
+ }
+ else if (obj instanceof Byte[])
+ {
+ byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
+ return str(bytes, charset);
+ }
+ else if (obj instanceof ByteBuffer)
+ {
+ return str((ByteBuffer) obj, charset);
+ }
+ return obj.toString();
+ }
+
+ /**
+ * 将byte数组转为字符串
+ *
+ * @param bytes byte数组
+ * @param charset 字符集
+ * @return 字符串
+ */
+ public static String str(byte[] bytes, String charset)
+ {
+ return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+ }
+
+ /**
+ * 解码字节码
+ *
+ * @param data 字符串
+ * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+ * @return 解码后的字符串
+ */
+ public static String str(byte[] data, Charset charset)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ if (null == charset)
+ {
+ return new String(data);
+ }
+ return new String(data, charset);
+ }
+
+ /**
+ * 将编码的byteBuffer数据转换为字符串
+ *
+ * @param data 数据
+ * @param charset 字符集,如果为空使用当前系统字符集
+ * @return 字符串
+ */
+ public static String str(ByteBuffer data, String charset)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ return str(data, Charset.forName(charset));
+ }
+
+ /**
+ * 将编码的byteBuffer数据转换为字符串
+ *
+ * @param data 数据
+ * @param charset 字符集,如果为空使用当前系统字符集
+ * @return 字符串
+ */
+ public static String str(ByteBuffer data, Charset charset)
+ {
+ if (null == charset)
+ {
+ charset = Charset.defaultCharset();
+ }
+ return charset.decode(data).toString();
+ }
+
+ // ----------------------------------------------------------------------- 全角半角转换
+ /**
+ * 半角转全角
+ *
+ * @param input String.
+ * @return 全角字符串.
+ */
+ public static String toSBC(String input)
+ {
+ return toSBC(input, null);
+ }
+
+ /**
+ * 半角转全角
+ *
+ * @param input String
+ * @param notConvertSet 不替换的字符集合
+ * @return 全角字符串.
+ */
+ public static String toSBC(String input, Set notConvertSet)
+ {
+ char[] c = input.toCharArray();
+ for (int i = 0; i < c.length; i++)
+ {
+ if (null != notConvertSet && notConvertSet.contains(c[i]))
+ {
+ // 跳过不替换的字符
+ continue;
+ }
+
+ if (c[i] == ' ')
+ {
+ c[i] = '\u3000';
+ }
+ else if (c[i] < '\177')
+ {
+ c[i] = (char) (c[i] + 65248);
+
+ }
+ }
+ return new String(c);
+ }
+
+ /**
+ * 全角转半角
+ *
+ * @param input String.
+ * @return 半角字符串
+ */
+ public static String toDBC(String input)
+ {
+ return toDBC(input, null);
+ }
+
+ /**
+ * 替换全角为半角
+ *
+ * @param text 文本
+ * @param notConvertSet 不替换的字符集合
+ * @return 替换后的字符
+ */
+ public static String toDBC(String text, Set notConvertSet)
+ {
+ char[] c = text.toCharArray();
+ for (int i = 0; i < c.length; i++)
+ {
+ if (null != notConvertSet && notConvertSet.contains(c[i]))
+ {
+ // 跳过不替换的字符
+ continue;
+ }
+
+ if (c[i] == '\u3000')
+ {
+ c[i] = ' ';
+ }
+ else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
+ {
+ c[i] = (char) (c[i] - 65248);
+ }
+ }
+ String returnString = new String(c);
+
+ return returnString;
+ }
+
+ /**
+ * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+ *
+ * @param n 数字
+ * @return 中文大写数字
+ */
+ public static String digitUppercase(double n)
+ {
+ String[] fraction = { "角", "分" };
+ String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
+ String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
+
+ String head = n < 0 ? "负" : "";
+ n = Math.abs(n);
+
+ String s = "";
+ for (int i = 0; i < fraction.length; i++)
+ {
+ s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+ }
+ if (s.length() < 1)
+ {
+ s = "整";
+ }
+ int integerPart = (int) Math.floor(n);
+
+ for (int i = 0; i < unit[0].length && integerPart > 0; i++)
+ {
+ String p = "";
+ for (int j = 0; j < unit[1].length && n > 0; j++)
+ {
+ p = digit[integerPart % 10] + unit[1][j] + p;
+ integerPart = integerPart / 10;
+ }
+ s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+ }
+ return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/core/text/StrFormatter.java b/bs-common/src/main/java/com/bs/common/core/text/StrFormatter.java
new file mode 100644
index 0000000..dbec7de
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/core/text/StrFormatter.java
@@ -0,0 +1,92 @@
+package com.bs.common.core.text;
+
+import com.bs.common.utils.StringUtils;
+
+/**
+ * 字符串格式化
+ *
+ * @author bs
+ */
+public class StrFormatter
+{
+ public static final String EMPTY_JSON = "{}";
+ public static final char C_BACKSLASH = '\\';
+ public static final char C_DELIM_START = '{';
+ public static final char C_DELIM_END = '}';
+
+ /**
+ * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ *
+ * @param strPattern 字符串模板
+ * @param argArray 参数列表
+ * @return 结果
+ */
+ public static String format(final String strPattern, final Object... argArray)
+ {
+ if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
+ {
+ return strPattern;
+ }
+ final int strPatternLength = strPattern.length();
+
+ // 初始化定义好的长度以获得更好的性能
+ StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+ int handledPosition = 0;
+ int delimIndex;// 占位符所在位置
+ for (int argIndex = 0; argIndex < argArray.length; argIndex++)
+ {
+ delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+ if (delimIndex == -1)
+ {
+ if (handledPosition == 0)
+ {
+ return strPattern;
+ }
+ else
+ { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+ sbuf.append(strPattern, handledPosition, strPatternLength);
+ return sbuf.toString();
+ }
+ }
+ else
+ {
+ if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
+ {
+ if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
+ {
+ // 转义符之前还有一个转义符,占位符依旧有效
+ sbuf.append(strPattern, handledPosition, delimIndex - 1);
+ sbuf.append(Convert.utf8Str(argArray[argIndex]));
+ handledPosition = delimIndex + 2;
+ }
+ else
+ {
+ // 占位符被转义
+ argIndex--;
+ sbuf.append(strPattern, handledPosition, delimIndex - 1);
+ sbuf.append(C_DELIM_START);
+ handledPosition = delimIndex + 1;
+ }
+ }
+ else
+ {
+ // 正常占位符
+ sbuf.append(strPattern, handledPosition, delimIndex);
+ sbuf.append(Convert.utf8Str(argArray[argIndex]));
+ handledPosition = delimIndex + 2;
+ }
+ }
+ }
+ // 加入最后一个占位符后所有的字符
+ sbuf.append(strPattern, handledPosition, strPattern.length());
+
+ return sbuf.toString();
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/BusinessStatus.java b/bs-common/src/main/java/com/bs/common/enums/BusinessStatus.java
new file mode 100644
index 0000000..08c7d85
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/BusinessStatus.java
@@ -0,0 +1,20 @@
+package com.bs.common.enums;
+
+/**
+ * 操作状态
+ *
+ * @author bs
+ *
+ */
+public enum BusinessStatus
+{
+ /**
+ * 成功
+ */
+ SUCCESS,
+
+ /**
+ * 失败
+ */
+ FAIL,
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/BusinessType.java b/bs-common/src/main/java/com/bs/common/enums/BusinessType.java
new file mode 100644
index 0000000..67d9a91
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/BusinessType.java
@@ -0,0 +1,59 @@
+package com.bs.common.enums;
+
+/**
+ * 业务操作类型
+ *
+ * @author bs
+ */
+public enum BusinessType
+{
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 新增
+ */
+ INSERT,
+
+ /**
+ * 修改
+ */
+ UPDATE,
+
+ /**
+ * 删除
+ */
+ DELETE,
+
+ /**
+ * 授权
+ */
+ GRANT,
+
+ /**
+ * 导出
+ */
+ EXPORT,
+
+ /**
+ * 导入
+ */
+ IMPORT,
+
+ /**
+ * 强退
+ */
+ FORCE,
+
+ /**
+ * 生成代码
+ */
+ GENCODE,
+
+ /**
+ * 清空数据
+ */
+ CLEAN,
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/DataSourceType.java b/bs-common/src/main/java/com/bs/common/enums/DataSourceType.java
new file mode 100644
index 0000000..0dec5b9
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/DataSourceType.java
@@ -0,0 +1,19 @@
+package com.bs.common.enums;
+
+/**
+ * 数据源
+ *
+ * @author bs
+ */
+public enum DataSourceType
+{
+ /**
+ * 主库
+ */
+ MASTER,
+
+ /**
+ * 从库
+ */
+ SLAVE
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/HttpMethod.java b/bs-common/src/main/java/com/bs/common/enums/HttpMethod.java
new file mode 100644
index 0000000..c6a53b7
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/HttpMethod.java
@@ -0,0 +1,36 @@
+package com.bs.common.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.lang.Nullable;
+
+/**
+ * 请求方式
+ *
+ * @author bs
+ */
+public enum HttpMethod
+{
+ GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
+
+ private static final Map mappings = new HashMap<>(16);
+
+ static
+ {
+ for (HttpMethod httpMethod : values())
+ {
+ mappings.put(httpMethod.name(), httpMethod);
+ }
+ }
+
+ @Nullable
+ public static HttpMethod resolve(@Nullable String method)
+ {
+ return (method != null ? mappings.get(method) : null);
+ }
+
+ public boolean matches(String method)
+ {
+ return (this == resolve(method));
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/LimitType.java b/bs-common/src/main/java/com/bs/common/enums/LimitType.java
new file mode 100644
index 0000000..da1074b
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/LimitType.java
@@ -0,0 +1,20 @@
+package com.bs.common.enums;
+
+/**
+ * 限流类型
+ *
+ * @author bs
+ */
+
+public enum LimitType
+{
+ /**
+ * 默认策略全局限流
+ */
+ DEFAULT,
+
+ /**
+ * 根据请求者IP进行限流
+ */
+ IP
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/OperatorType.java b/bs-common/src/main/java/com/bs/common/enums/OperatorType.java
new file mode 100644
index 0000000..c47c9d4
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/OperatorType.java
@@ -0,0 +1,24 @@
+package com.bs.common.enums;
+
+/**
+ * 操作人类别
+ *
+ * @author bs
+ */
+public enum OperatorType
+{
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 后台用户
+ */
+ MANAGE,
+
+ /**
+ * 手机端用户
+ */
+ MOBILE
+}
diff --git a/bs-common/src/main/java/com/bs/common/enums/UserStatus.java b/bs-common/src/main/java/com/bs/common/enums/UserStatus.java
new file mode 100644
index 0000000..047b855
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/enums/UserStatus.java
@@ -0,0 +1,30 @@
+package com.bs.common.enums;
+
+/**
+ * 用户状态
+ *
+ * @author bs
+ */
+public enum UserStatus
+{
+ OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除");
+
+ private final String code;
+ private final String info;
+
+ UserStatus(String code, String info)
+ {
+ this.code = code;
+ this.info = info;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public String getInfo()
+ {
+ return info;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/DemoModeException.java b/bs-common/src/main/java/com/bs/common/exception/DemoModeException.java
new file mode 100644
index 0000000..2840d39
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/DemoModeException.java
@@ -0,0 +1,15 @@
+package com.bs.common.exception;
+
+/**
+ * 演示模式异常
+ *
+ * @author bs
+ */
+public class DemoModeException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ public DemoModeException()
+ {
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/GlobalException.java b/bs-common/src/main/java/com/bs/common/exception/GlobalException.java
new file mode 100644
index 0000000..bc8d687
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/GlobalException.java
@@ -0,0 +1,58 @@
+package com.bs.common.exception;
+
+/**
+ * 全局异常
+ *
+ * @author bs
+ */
+public class GlobalException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 错误明细,内部调试错误
+ *
+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计
+ */
+ private String detailMessage;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public GlobalException()
+ {
+ }
+
+ public GlobalException(String message)
+ {
+ this.message = message;
+ }
+
+ public String getDetailMessage()
+ {
+ return detailMessage;
+ }
+
+ public GlobalException setDetailMessage(String detailMessage)
+ {
+ this.detailMessage = detailMessage;
+ return this;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public GlobalException setMessage(String message)
+ {
+ this.message = message;
+ return this;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/ServiceException.java b/bs-common/src/main/java/com/bs/common/exception/ServiceException.java
new file mode 100644
index 0000000..ec3e7a4
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/ServiceException.java
@@ -0,0 +1,74 @@
+package com.bs.common.exception;
+
+/**
+ * 业务异常
+ *
+ * @author bs
+ */
+public final class ServiceException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误码
+ */
+ private Integer code;
+
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 错误明细,内部调试错误
+ *
+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计
+ */
+ private String detailMessage;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public ServiceException()
+ {
+ }
+
+ public ServiceException(String message)
+ {
+ this.message = message;
+ }
+
+ public ServiceException(String message, Integer code)
+ {
+ this.message = message;
+ this.code = code;
+ }
+
+ public String getDetailMessage()
+ {
+ return detailMessage;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public Integer getCode()
+ {
+ return code;
+ }
+
+ public ServiceException setMessage(String message)
+ {
+ this.message = message;
+ return this;
+ }
+
+ public ServiceException setDetailMessage(String detailMessage)
+ {
+ this.detailMessage = detailMessage;
+ return this;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/UtilException.java b/bs-common/src/main/java/com/bs/common/exception/UtilException.java
new file mode 100644
index 0000000..df7a1ef
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/UtilException.java
@@ -0,0 +1,26 @@
+package com.bs.common.exception;
+
+/**
+ * 工具类异常
+ *
+ * @author bs
+ */
+public class UtilException extends RuntimeException
+{
+ private static final long serialVersionUID = 8247610319171014183L;
+
+ public UtilException(Throwable e)
+ {
+ super(e.getMessage(), e);
+ }
+
+ public UtilException(String message)
+ {
+ super(message);
+ }
+
+ public UtilException(String message, Throwable throwable)
+ {
+ super(message, throwable);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/base/BaseException.java b/bs-common/src/main/java/com/bs/common/exception/base/BaseException.java
new file mode 100644
index 0000000..9506f66
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/base/BaseException.java
@@ -0,0 +1,97 @@
+package com.bs.common.exception.base;
+
+import com.bs.common.utils.MessageUtils;
+import com.bs.common.utils.StringUtils;
+
+/**
+ * 基础异常
+ *
+ * @author bs
+ */
+public class BaseException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 所属模块
+ */
+ private String module;
+
+ /**
+ * 错误码
+ */
+ private String code;
+
+ /**
+ * 错误码对应的参数
+ */
+ private Object[] args;
+
+ /**
+ * 错误消息
+ */
+ private String defaultMessage;
+
+ public BaseException(String module, String code, Object[] args, String defaultMessage)
+ {
+ this.module = module;
+ this.code = code;
+ this.args = args;
+ this.defaultMessage = defaultMessage;
+ }
+
+ public BaseException(String module, String code, Object[] args)
+ {
+ this(module, code, args, null);
+ }
+
+ public BaseException(String module, String defaultMessage)
+ {
+ this(module, null, null, defaultMessage);
+ }
+
+ public BaseException(String code, Object[] args)
+ {
+ this(null, code, args, null);
+ }
+
+ public BaseException(String defaultMessage)
+ {
+ this(null, null, null, defaultMessage);
+ }
+
+ @Override
+ public String getMessage()
+ {
+ String message = null;
+ if (!StringUtils.isEmpty(code))
+ {
+ message = MessageUtils.message(code, args);
+ }
+ if (message == null)
+ {
+ message = defaultMessage;
+ }
+ return message;
+ }
+
+ public String getModule()
+ {
+ return module;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public Object[] getArgs()
+ {
+ return args;
+ }
+
+ public String getDefaultMessage()
+ {
+ return defaultMessage;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/file/FileException.java b/bs-common/src/main/java/com/bs/common/exception/file/FileException.java
new file mode 100644
index 0000000..778277f
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/file/FileException.java
@@ -0,0 +1,19 @@
+package com.bs.common.exception.file;
+
+import com.bs.common.exception.base.BaseException;
+
+/**
+ * 文件信息异常类
+ *
+ * @author bs
+ */
+public class FileException extends BaseException
+{
+ private static final long serialVersionUID = 1L;
+
+ public FileException(String code, Object[] args)
+ {
+ super("file", code, args, null);
+ }
+
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/file/FileNameLengthLimitExceededException.java b/bs-common/src/main/java/com/bs/common/exception/file/FileNameLengthLimitExceededException.java
new file mode 100644
index 0000000..74d915c
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/file/FileNameLengthLimitExceededException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.file;
+
+/**
+ * 文件名称超长限制异常类
+ *
+ * @author bs
+ */
+public class FileNameLengthLimitExceededException extends FileException
+{
+ private static final long serialVersionUID = 1L;
+
+ public FileNameLengthLimitExceededException(int defaultFileNameLength)
+ {
+ super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/file/FileSizeLimitExceededException.java b/bs-common/src/main/java/com/bs/common/exception/file/FileSizeLimitExceededException.java
new file mode 100644
index 0000000..6d82643
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/file/FileSizeLimitExceededException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.file;
+
+/**
+ * 文件名大小限制异常类
+ *
+ * @author bs
+ */
+public class FileSizeLimitExceededException extends FileException
+{
+ private static final long serialVersionUID = 1L;
+
+ public FileSizeLimitExceededException(long defaultMaxSize)
+ {
+ super("upload.exceed.maxSize", new Object[] { defaultMaxSize });
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/file/FileUploadException.java b/bs-common/src/main/java/com/bs/common/exception/file/FileUploadException.java
new file mode 100644
index 0000000..462a5cb
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/file/FileUploadException.java
@@ -0,0 +1,61 @@
+package com.bs.common.exception.file;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * 文件上传异常类
+ *
+ * @author bs
+ */
+public class FileUploadException extends Exception
+{
+
+ private static final long serialVersionUID = 1L;
+
+ private final Throwable cause;
+
+ public FileUploadException()
+ {
+ this(null, null);
+ }
+
+ public FileUploadException(final String msg)
+ {
+ this(msg, null);
+ }
+
+ public FileUploadException(String msg, Throwable cause)
+ {
+ super(msg);
+ this.cause = cause;
+ }
+
+ @Override
+ public void printStackTrace(PrintStream stream)
+ {
+ super.printStackTrace(stream);
+ if (cause != null)
+ {
+ stream.println("Caused by:");
+ cause.printStackTrace(stream);
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintWriter writer)
+ {
+ super.printStackTrace(writer);
+ if (cause != null)
+ {
+ writer.println("Caused by:");
+ cause.printStackTrace(writer);
+ }
+ }
+
+ @Override
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/file/InvalidExtensionException.java b/bs-common/src/main/java/com/bs/common/exception/file/InvalidExtensionException.java
new file mode 100644
index 0000000..0cb5bc6
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/file/InvalidExtensionException.java
@@ -0,0 +1,80 @@
+package com.bs.common.exception.file;
+
+import java.util.Arrays;
+
+/**
+ * 文件上传 误异常类
+ *
+ * @author bs
+ */
+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 + "]后缀[" + 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);
+ }
+ }
+
+ public static class InvalidVideoExtensionException extends InvalidExtensionException
+ {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename)
+ {
+ super(allowedExtension, extension, filename);
+ }
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/job/TaskException.java b/bs-common/src/main/java/com/bs/common/exception/job/TaskException.java
new file mode 100644
index 0000000..fdbee48
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/job/TaskException.java
@@ -0,0 +1,34 @@
+package com.bs.common.exception.job;
+
+/**
+ * 计划策略异常
+ *
+ * @author bs
+ */
+public class TaskException extends Exception
+{
+ private static final long serialVersionUID = 1L;
+
+ private Code code;
+
+ public TaskException(String msg, Code code)
+ {
+ this(msg, code, null);
+ }
+
+ public TaskException(String msg, Code code, Exception nestedEx)
+ {
+ super(msg, nestedEx);
+ this.code = code;
+ }
+
+ public Code getCode()
+ {
+ return code;
+ }
+
+ public enum Code
+ {
+ TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/BlackListException.java b/bs-common/src/main/java/com/bs/common/exception/user/BlackListException.java
new file mode 100644
index 0000000..268fbef
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/BlackListException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.user;
+
+/**
+ * 黑名单IP异常类
+ *
+ * @author bs
+ */
+public class BlackListException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public BlackListException()
+ {
+ super("login.blocked", null);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/CaptchaException.java b/bs-common/src/main/java/com/bs/common/exception/user/CaptchaException.java
new file mode 100644
index 0000000..bc8d5cb
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/CaptchaException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.user;
+
+/**
+ * 验证码错误异常类
+ *
+ * @author bs
+ */
+public class CaptchaException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaException()
+ {
+ super("user.jcaptcha.error", null);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/CaptchaExpireException.java b/bs-common/src/main/java/com/bs/common/exception/user/CaptchaExpireException.java
new file mode 100644
index 0000000..5e93d41
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/CaptchaExpireException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.user;
+
+/**
+ * 验证码失效异常类
+ *
+ * @author bs
+ */
+public class CaptchaExpireException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaExpireException()
+ {
+ super("user.jcaptcha.expire", null);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/UserException.java b/bs-common/src/main/java/com/bs/common/exception/user/UserException.java
new file mode 100644
index 0000000..93ffd2c
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/UserException.java
@@ -0,0 +1,18 @@
+package com.bs.common.exception.user;
+
+import com.bs.common.exception.base.BaseException;
+
+/**
+ * 用户信息异常类
+ *
+ * @author bs
+ */
+public class UserException extends BaseException
+{
+ private static final long serialVersionUID = 1L;
+
+ public UserException(String code, Object[] args)
+ {
+ super("user", code, args, null);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/UserNotExistsException.java b/bs-common/src/main/java/com/bs/common/exception/user/UserNotExistsException.java
new file mode 100644
index 0000000..2862e11
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/UserNotExistsException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.user;
+
+/**
+ * 用户不存在异常类
+ *
+ * @author bs
+ */
+public class UserNotExistsException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public UserNotExistsException()
+ {
+ super("user.not.exists", null);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/UserPasswordNotMatchException.java b/bs-common/src/main/java/com/bs/common/exception/user/UserPasswordNotMatchException.java
new file mode 100644
index 0000000..f5af1bd
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/UserPasswordNotMatchException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.user;
+
+/**
+ * 用户密码不正确或不符合规范异常类
+ *
+ * @author bs
+ */
+public class UserPasswordNotMatchException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public UserPasswordNotMatchException()
+ {
+ super("user.password.not.match", null);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/exception/user/UserPasswordRetryLimitExceedException.java b/bs-common/src/main/java/com/bs/common/exception/user/UserPasswordRetryLimitExceedException.java
new file mode 100644
index 0000000..e4f0fe2
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/exception/user/UserPasswordRetryLimitExceedException.java
@@ -0,0 +1,16 @@
+package com.bs.common.exception.user;
+
+/**
+ * 用户错误最大次数异常类
+ *
+ * @author bs
+ */
+public class UserPasswordRetryLimitExceedException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime)
+ {
+ super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime });
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/filter/PortFilter.java b/bs-common/src/main/java/com/bs/common/filter/PortFilter.java
new file mode 100644
index 0000000..502e894
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/filter/PortFilter.java
@@ -0,0 +1,47 @@
+package com.bs.common.filter;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.bs.common.utils.ServletUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.*;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Repeatable 过滤器
+ *
+ * @author ruoyi
+ */
+public class PortFilter implements Filter {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PortFilter.class);
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ Map paramMap = request.getParameterMap();
+ //获取请求参数
+ String queryString = ServletUtils.getRequest().getQueryString();
+ //同一次请求,放同一个log。info 中,
+ //防止同时请求多个接口,把同一个请求信息冲散,不便查看
+ LOGGER.info("\r\n请求的链接:" + ServletUtils.getRequest().getRequestURL().toString()
+ +"\r\n"+"请求方式:" + ServletUtils.getRequest().getMethod()
+ +"\r\n"+"请求的接口是:" + ServletUtils.getRequest().getRequestURI()
+ +"\r\n"+"请求参数map:{}", ServletUtils.getParamMap(request)
+ +"\r\n"+"请求参数map:{}", new JSONObject(paramMap)
+ +"\r\n"+"请求参数:{}",queryString);
+
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/filter/PropertyPreExcludeFilter.java b/bs-common/src/main/java/com/bs/common/filter/PropertyPreExcludeFilter.java
new file mode 100644
index 0000000..fc6c46b
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/filter/PropertyPreExcludeFilter.java
@@ -0,0 +1,24 @@
+package com.bs.common.filter;
+
+import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
+
+/**
+ * 排除JSON敏感属性
+ *
+ * @author bs
+ */
+public class PropertyPreExcludeFilter extends SimplePropertyPreFilter
+{
+ public PropertyPreExcludeFilter()
+ {
+ }
+
+ public PropertyPreExcludeFilter addExcludes(String... filters)
+ {
+ for (int i = 0; i < filters.length; i++)
+ {
+ this.getExcludes().add(filters[i]);
+ }
+ return this;
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/filter/RepeatableFilter.java b/bs-common/src/main/java/com/bs/common/filter/RepeatableFilter.java
new file mode 100644
index 0000000..fb286ad
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/filter/RepeatableFilter.java
@@ -0,0 +1,52 @@
+package com.bs.common.filter;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.http.MediaType;
+import com.bs.common.utils.StringUtils;
+
+/**
+ * Repeatable 过滤器
+ *
+ * @author bs
+ */
+public class RepeatableFilter implements Filter
+{
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ ServletRequest requestWrapper = null;
+ if (request instanceof HttpServletRequest
+ && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
+ {
+ requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
+ }
+ if (null == requestWrapper)
+ {
+ chain.doFilter(request, response);
+ }
+ else
+ {
+ chain.doFilter(requestWrapper, response);
+ }
+ }
+
+ @Override
+ public void destroy()
+ {
+
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/filter/RepeatedlyRequestWrapper.java b/bs-common/src/main/java/com/bs/common/filter/RepeatedlyRequestWrapper.java
new file mode 100644
index 0000000..45345b0
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/filter/RepeatedlyRequestWrapper.java
@@ -0,0 +1,76 @@
+package com.bs.common.filter;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import com.bs.common.utils.http.HttpHelper;
+import com.bs.common.constant.Constants;
+
+/**
+ * 构建可重复读取inputStream的request
+ *
+ * @author bs
+ */
+public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
+{
+ private final byte[] body;
+
+ public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
+ {
+ super(request);
+ request.setCharacterEncoding(Constants.UTF8);
+ response.setCharacterEncoding(Constants.UTF8);
+
+ body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8);
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException
+ {
+ return new BufferedReader(new InputStreamReader(getInputStream()));
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException
+ {
+ final ByteArrayInputStream bais = new ByteArrayInputStream(body);
+ return new ServletInputStream()
+ {
+ @Override
+ public int read() throws IOException
+ {
+ return bais.read();
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ return body.length;
+ }
+
+ @Override
+ public boolean isFinished()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isReady()
+ {
+ return false;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener)
+ {
+
+ }
+ };
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/filter/XssFilter.java b/bs-common/src/main/java/com/bs/common/filter/XssFilter.java
new file mode 100644
index 0000000..a4b65d3
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/filter/XssFilter.java
@@ -0,0 +1,71 @@
+package com.bs.common.filter;
+
+import com.bs.common.enums.HttpMethod;
+import com.bs.common.utils.StringUtils;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 防止XSS攻击的过滤器
+ *
+ * @author bs
+ */
+public class XssFilter implements Filter
+{
+ /**
+ * 排除链接
+ */
+ public List excludes = new ArrayList<>();
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ String tempExcludes = filterConfig.getInitParameter("excludes");
+ if (StringUtils.isNotEmpty(tempExcludes))
+ {
+ String[] url = tempExcludes.split(",");
+ for (int i = 0; url != null && i < url.length; i++)
+ {
+ excludes.add(url[i]);
+ }
+ }
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse resp = (HttpServletResponse) response;
+ if (handleExcludeURL(req, resp))
+ {
+ chain.doFilter(request, response);
+ return;
+ }
+ XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
+ chain.doFilter(xssRequest, response);
+ }
+
+ private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
+ {
+ String url = request.getServletPath();
+ String method = request.getMethod();
+ // GET DELETE 不过滤
+ if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))
+ {
+ return true;
+ }
+ return StringUtils.matches(url, excludes);
+ }
+
+ @Override
+ public void destroy()
+ {
+
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/filter/XssHttpServletRequestWrapper.java b/bs-common/src/main/java/com/bs/common/filter/XssHttpServletRequestWrapper.java
new file mode 100644
index 0000000..68ca842
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/filter/XssHttpServletRequestWrapper.java
@@ -0,0 +1,111 @@
+package com.bs.common.filter;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import com.bs.common.utils.StringUtils;
+import com.bs.common.utils.html.EscapeUtil;
+
+/**
+ * XSS过滤处理
+ *
+ * @author bs
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
+{
+ /**
+ * @param request
+ */
+ public XssHttpServletRequestWrapper(HttpServletRequest request)
+ {
+ super(request);
+ }
+
+ @Override
+ public String[] getParameterValues(String name)
+ {
+ String[] values = super.getParameterValues(name);
+ if (values != null)
+ {
+ int length = values.length;
+ String[] escapesValues = new String[length];
+ for (int i = 0; i < length; i++)
+ {
+ // 防xss攻击和过滤前后空格
+ escapesValues[i] = EscapeUtil.clean(values[i]).trim();
+ }
+ return escapesValues;
+ }
+ return super.getParameterValues(name);
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException
+ {
+ // 非json类型,直接返回
+ if (!isJsonRequest())
+ {
+ return super.getInputStream();
+ }
+
+ // 为空,直接返回
+ String json = IOUtils.toString(super.getInputStream(), "utf-8");
+ if (StringUtils.isEmpty(json))
+ {
+ return super.getInputStream();
+ }
+
+ // xss过滤
+ json = EscapeUtil.clean(json).trim();
+ byte[] jsonBytes = json.getBytes("utf-8");
+ final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);
+ return new ServletInputStream()
+ {
+ @Override
+ public boolean isFinished()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isReady()
+ {
+ return true;
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ return jsonBytes.length;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener)
+ {
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ return bis.read();
+ }
+ };
+ }
+
+ /**
+ * 是否是Json请求
+ *
+ * @param request
+ */
+ public boolean isJsonRequest()
+ {
+ String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
+ return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/mybatis/handler/DefaultDBFieldHandler.java b/bs-common/src/main/java/com/bs/common/mybatis/handler/DefaultDBFieldHandler.java
new file mode 100644
index 0000000..4c25141
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/mybatis/handler/DefaultDBFieldHandler.java
@@ -0,0 +1,76 @@
+package com.bs.common.mybatis.handler;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.bs.common.utils.SecurityUtils;
+import org.apache.ibatis.reflection.MetaObject;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * 通用参数填充实现类
+ *
+ * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值
+ *
+ * @author hexiaowu
+ */
+public class DefaultDBFieldHandler implements MetaObjectHandler {
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ /**
+ * createBy 创建人
+ */
+ boolean createByOf = metaObject.hasSetter("createBy");
+ if(createByOf){
+ this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
+ }
+
+ /**
+ * createDate 创建时间
+ */
+ boolean createTime = metaObject.hasSetter("createTime");
+ if(createTime){
+ this.setFieldValByName("createTime", LocalDateTime.now() , metaObject);
+ }
+ /**
+ * createDate 创建时间
+ */
+ boolean delFlag = metaObject.hasSetter("delFlag");
+ if(delFlag){
+ this.setFieldValByName("delFlag", "0", metaObject);
+ }
+ /**
+ *部门id
+ */
+ boolean deptId = metaObject.hasSetter("createDept");
+ if(deptId){
+ this.setFieldValByName("createDept", SecurityUtils.getDeptId(), metaObject);
+ }
+
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+
+ /**
+ * updateBy 更新人
+ */
+ boolean updateByOf = metaObject.hasSetter("updateBy");
+ if(updateByOf){
+ if (Objects.nonNull(SecurityUtils.getUsername())) {
+ this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
+ }
+ }
+
+
+ /**
+ * updateDate 更新时间
+ */
+
+ boolean updateTimeOf = metaObject.hasSetter("updateTime");
+ if(updateTimeOf){
+ this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
+ }
+ }
+}
diff --git a/bs-common/src/main/java/com/bs/common/mybatis/mapper/BaseMapperX.java b/bs-common/src/main/java/com/bs/common/mybatis/mapper/BaseMapperX.java
new file mode 100644
index 0000000..529e708
--- /dev/null
+++ b/bs-common/src/main/java/com/bs/common/mybatis/mapper/BaseMapperX.java
@@ -0,0 +1,94 @@
+package com.bs.common.mybatis.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.bs.common.mybatis.pojo.PageParam;
+import com.bs.common.mybatis.pojo.PageResult;
+import com.bs.common.mybatis.util.MyBatisUtils;
+import com.github.yulichang.base.MPJBaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
+ */
+public interface BaseMapperX extends MPJBaseMapper {
+
+ default PageResult selectPage(PageParam pageParam, @Param("ew") Wrapper queryWrapper) {
+ // MyBatis Plus 查询
+ IPage