Repository: gnanquanmama/tropical-fish
Branch: master
Commit: c06be7a4398c
Files: 198
Total size: 357.2 KB
Directory structure:
gitextract_d6am_5fj/
├── .gitignore
├── LICENSE
├── README.md
├── _docs/
│ └── fish_mysql.sql
├── applet/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── mcoding/
│ │ └── applet/
│ │ ├── AppletApplication.java
│ │ ├── auth/
│ │ │ ├── AppAuthController.java
│ │ │ ├── WechatAuthController.java
│ │ │ ├── base/
│ │ │ │ ├── LoginRequired.java
│ │ │ │ ├── LoginRequiredArgumentResolver.java
│ │ │ │ └── config/
│ │ │ │ ├── AuthConfig.java
│ │ │ │ ├── AuthInterceptor.java
│ │ │ │ └── LoginRequiredConfig.java
│ │ │ ├── business/
│ │ │ │ ├── RegisterBo.java
│ │ │ │ ├── UserInfoBo.java
│ │ │ │ └── resp/
│ │ │ │ ├── AccessTokenRespEntity.java
│ │ │ │ ├── JsCode2SessionRespEntity.java
│ │ │ │ └── WxCodeUnlimitedResponse.java
│ │ │ ├── dao/
│ │ │ │ └── BaseUserTokenMapper.java
│ │ │ ├── dto/
│ │ │ │ ├── BindingStoreDto.java
│ │ │ │ ├── CreateUserDto.java
│ │ │ │ ├── PhoneNumberDto.java
│ │ │ │ └── RegisterDto.java
│ │ │ ├── entity/
│ │ │ │ └── BaseUserToken.java
│ │ │ ├── manager/
│ │ │ │ ├── WechatClient.java
│ │ │ │ └── impl/
│ │ │ │ └── WechatClientImpl.java
│ │ │ ├── service/
│ │ │ │ ├── BaseUserTokenService.java
│ │ │ │ ├── WechatAuthService.java
│ │ │ │ └── impl/
│ │ │ │ ├── BaseUserTokenServiceImpl.java
│ │ │ │ └── WechatAuthServiceImpl.java
│ │ │ └── util/
│ │ │ └── LoginUserUtils.java
│ │ └── order/
│ │ └── component/
│ │ ├── ActivityCodeController.java
│ │ ├── ActivityOrderBizCodeGenerator.java
│ │ └── TargetCodeEnum.java
│ └── resources/
│ ├── application-dev.properties
│ ├── application-prd.properties
│ ├── application.properties
│ ├── logback-spring.xml
│ └── prop/
│ └── redisson-dev.yaml
├── backend/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── mcoding/
│ │ ├── BackendApplication.java
│ │ └── modular/
│ │ ├── auth/
│ │ │ ├── config/
│ │ │ │ ├── AuthConfig.java
│ │ │ │ ├── AuthInterceptor.java
│ │ │ │ └── LoginRequiredConfig.java
│ │ │ ├── controller/
│ │ │ │ └── AppAuthController.java
│ │ │ └── support/
│ │ │ ├── LoginRequired.java
│ │ │ ├── LoginRequiredArgumentResolver.java
│ │ │ └── LoginUserUtils.java
│ │ ├── biz/
│ │ │ └── user/
│ │ │ └── controller/
│ │ │ ├── BizUserController.java
│ │ │ └── UserDataListener.java
│ │ ├── rule/
│ │ │ ├── ACmp.java
│ │ │ ├── BCmp.java
│ │ │ ├── BizFlow.java
│ │ │ ├── CCmp.java
│ │ │ └── RuleFlowController.java
│ │ ├── search/
│ │ │ ├── controller/
│ │ │ │ └── ProductSpuController.java
│ │ │ ├── dao/
│ │ │ │ └── ProductSpuMapper.java
│ │ │ ├── entity/
│ │ │ │ └── ProductSpu.java
│ │ │ └── service/
│ │ │ ├── ProductSpuService.java
│ │ │ └── impl/
│ │ │ └── ProductSpuServiceImpl.java
│ │ ├── system/
│ │ │ └── user/
│ │ │ ├── controller/
│ │ │ │ └── SysUserController.java
│ │ │ ├── dao/
│ │ │ │ └── SysUserMapper.java
│ │ │ ├── entity/
│ │ │ │ └── SysUser.java
│ │ │ └── service/
│ │ │ ├── SysUserService.java
│ │ │ └── impl/
│ │ │ └── SysUserServiceImpl.java
│ │ └── tech/
│ │ └── job/
│ │ ├── ActivityStatusUpdateJob.java
│ │ └── config/
│ │ ├── XxlJobConfig.java
│ │ └── XxlJobPropertiesConfig.java
│ └── resources/
│ ├── application-dev.properties
│ ├── application.properties
│ ├── config/
│ │ └── flow.el.xml
│ ├── logback-spring.xml
│ ├── prop/
│ │ └── redisson-dev.yaml
│ └── template/
│ └── UserTemplate.ftl
├── base-common/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── mcoding/
│ └── base/
│ └── common/
│ ├── exception/
│ │ ├── BizException.java
│ │ ├── CommonException.java
│ │ └── SysException.java
│ ├── pattern/
│ │ ├── command/
│ │ │ ├── CommandInvoker.java
│ │ │ ├── ICommand.java
│ │ │ └── ICommandInvoker.java
│ │ ├── filterchain/
│ │ │ ├── Filter.java
│ │ │ ├── FilterContext.java
│ │ │ └── Target.java
│ │ └── pipeline/
│ │ ├── Pipeline.java
│ │ ├── StandardPipeline.java
│ │ └── Value.java
│ └── util/
│ ├── Assert.java
│ ├── bean/
│ │ └── BeanMapperUtils.java
│ ├── collection/
│ │ └── MapUtils.java
│ ├── constant/
│ │ ├── MdcConstants.java
│ │ └── SysConstants.java
│ ├── date/
│ │ ├── DateTimeUtils.java
│ │ ├── DateUtils.java
│ │ └── DateValidator.java
│ ├── encryption/
│ │ └── Md5Utils.java
│ ├── excel/
│ │ ├── ExcelProperty.java
│ │ ├── ExcelUtils.java
│ │ ├── TitleAndModelKey.java
│ │ └── converter/
│ │ ├── BigDecimalConverter.java
│ │ ├── ConverterFactory.java
│ │ ├── DateConverter.java
│ │ ├── IntegerConverter.java
│ │ ├── LongConverter.java
│ │ ├── ObjToStrConverter.java
│ │ ├── StrToObjConverter.java
│ │ └── YesOrNoIntegerConverter.java
│ ├── id/
│ │ ├── IdGenerator.java
│ │ └── RandomIdGenerator.java
│ ├── image/
│ │ ├── ImageUtils.java
│ │ └── ImageWatermarkUtils.java
│ ├── math/
│ │ ├── BigDecimalUtils.java
│ │ ├── BigDecimalWrapper.java
│ │ ├── NumberMoneyConvertUtil.java
│ │ └── RMBUtil.java
│ ├── pdf/
│ │ └── FtlToPdfUtil.java
│ ├── reflect/
│ │ └── ReflectUtils.java
│ └── wechat/
│ ├── AES.java
│ ├── WXBizDataCrypt.java
│ ├── WxUserInfo.java
│ └── WxWaterMark.java
├── base-core/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── mcoding/
│ └── base/
│ └── core/
│ ├── cache/
│ │ ├── RCacheAspect.java
│ │ ├── RCacheEvict.java
│ │ └── RCacheable.java
│ ├── config/
│ │ ├── ControllerConfig.java
│ │ ├── EsClientConfig.java
│ │ ├── FilterConfig.java
│ │ ├── JavaSimonConfig.java
│ │ ├── MybatisPlusConfig.java
│ │ └── SwaggerConfig.java
│ ├── doc/
│ │ ├── EventNode.java
│ │ ├── EventNodeContainer.java
│ │ ├── EventNodeStack.java
│ │ ├── EventTraceAspect.java
│ │ ├── Phase.java
│ │ ├── Process.java
│ │ ├── Step.java
│ │ ├── controller/
│ │ │ ├── DocumentController.java
│ │ │ ├── TreeBuilder.java
│ │ │ └── dto/
│ │ │ └── TreeNode.java
│ │ └── filter/
│ │ └── MethodInvokeTreeFiler.java
│ ├── http/
│ │ └── HttpComponentConfig.java
│ ├── log/
│ │ ├── MdcAspect.java
│ │ ├── MdcLog.java
│ │ ├── MybatisLogImpl.java
│ │ ├── TraceRequestFiler.java
│ │ └── WebLogAspect.java
│ ├── orm/
│ │ ├── DslParser.java
│ │ ├── Keyword.java
│ │ ├── Like.java
│ │ ├── MetaModelField.java
│ │ ├── MetaModelUtils.java
│ │ ├── OprEnum.java
│ │ ├── OrderByAsc.java
│ │ ├── OrderByDesc.java
│ │ ├── ParseHandler.java
│ │ ├── ParseOrderByCondHandler.java
│ │ ├── ParsePageHandler.java
│ │ ├── ParseSearchCondHandler.java
│ │ ├── ParseWhereCondHandler.java
│ │ ├── ParserContext.java
│ │ ├── QueryKeyWord.java
│ │ └── WhereCondition.java
│ ├── rate/
│ │ └── RateLimitFilter.java
│ ├── rest/
│ │ ├── BoolObject.java
│ │ ├── IdObject.java
│ │ ├── PageView.java
│ │ ├── ResponseCode.java
│ │ └── ResponseResult.java
│ └── spring/
│ ├── AopUtils.java
│ ├── GglibBeanMap.java
│ └── SpringContextHolder.java
├── base-generator/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── mcoding/
│ │ └── base/
│ │ └── generator/
│ │ └── CodeGenerator.java
│ └── resources/
│ └── templates/
│ └── mybatis-plus/
│ ├── controller.java.ftl
│ └── entity.java.ftl
├── biz-component/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com.mcoding.base.component/
│ │ ├── ComponentApplication.java
│ │ ├── generatecode/
│ │ │ ├── dao/
│ │ │ │ └── BaseGenerateCodeDao.java
│ │ │ ├── domain/
│ │ │ │ └── CommonBizCodeGenerator.java
│ │ │ ├── entity/
│ │ │ │ └── BaseGenerateCode.java
│ │ │ ├── service/
│ │ │ │ ├── BaseGenerateCodeService.java
│ │ │ │ └── impl/
│ │ │ │ └── BaseGenerateCodeServiceImpl.java
│ │ │ └── strategy/
│ │ │ ├── AutoIncrementStrategy.java
│ │ │ ├── DateIncrementStrategy.java
│ │ │ └── GenerateStrategy.java
│ │ └── shorturl/
│ │ ├── controller/
│ │ │ └── ShortUrlController.java
│ │ └── domain/
│ │ └── ShortUrlGenerator.java
│ └── resources/
│ ├── application-dev.properties
│ ├── application.properties
│ └── prop/
│ └── redisson-dev.yaml
├── biz-user/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── mcoding/
│ └── base/
│ └── user/
│ ├── dao/
│ │ └── BaseUserMapper.java
│ ├── entity/
│ │ └── BaseUser.java
│ └── service/
│ ├── BaseUserService.java
│ └── impl/
│ └── BaseUserServiceImpl.java
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Compiled class file
*.class
*.iml
*.idea
target/
logs/
# Log file
*.log
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*velocity.log*
# Eclipse #
.classpath
.project
.settings/
.DS_Store
_dockerCerts/
.factorypath
generatortmp/*
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 λx.wzt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Tropical Fish
Pragmatic 风格的 Java EE 后端开发脚手架。 基于 SpringBoot,技术选型采用主流的技术框架(Mybatis-Plus,Redisson,Xxl-job,Swagger)。开箱即用,提高研发效能。
多家公司线上产品使用了该脚手架。国内某知名日化企业,已把该脚手架作为基础脚手架,支撑数字化产品的研发。
### 项目特点
1. 自定义 DSL 查询语法。配合 generator 模块,研发人员定义表结构之后,逆向生成代码,单表情况下的CRUD,包括分页查询,可不用写一行代码,就完成开发任务。减轻后端研发人员开发压力,提高研发效率。分页查询语法 在 **自定义 DSL 查询语法** 有做详细的说明。
2. 自研 Excel 报表导入导出工具。 配合自定义查询语法,让导出 Excel 功能的开发和普通条件查询一样简单。高效开发报表导出功能。使用方法参照:BaseUserController.exportByExcel 。
3. 自研 分布式业务编码 生成服务。业务编码可批量生成。生成和使用过程,都是线程安全。 详细参照方法:ActivityOrderBizCodeGenerator.generateNextCode ;generateBizCodeList 。
4. 自定义缓存 @RCacheable 注解,实现分布式缓存。支持 Spel语法,可直接指定 expireTime 。
示例:@RCacheable(key = "dmt::miniprogram::token", secKey = "#token", ttl = 1, timeUnit = TimeUnit.DAYS)
5. 自定义注解 @LoginRequired 注解,可以自动装配当前操作人实体。该注解的意义在于,消除在每个 controller 方法需要手动获取当前操作人的重复性的代码。
6. 自定义 service 方法级别文档生成规则和实现。某种程度上缓解研发人员不爱写文档,又抱怨接手新项目没有文档的尴尬处境。 在**方法调用树示例** 有相应的 json 视图可以看到调用树的数据结构。
> 参考 <从码农到工匠> 控制代码复杂度的做法。复杂的业务的流程可拆分为多个阶段,每个阶段下有多个子步骤。 自定义注解,过程 @Process, 阶段 @Phase,步骤 @Step。在业务方法的阶段和步骤上加上相应的注解,即可根据请求返回的 TraceId 获取 service 级别的方法调用树。
> 研发人员需要按照定义规则流水线化,组件化设计代码,再加上必要的注解,runtime 状态下,就可以得到一颗拥有层次结构的方法调用树,得到复杂业务逻辑的主干架构。每个树结点有 Java 类方法,行数等信息,所见即所得。
#### 自定义 DSL 查询语法
> 查询条件语法
> ```json
> {
> "current": "页码",
> "size": "页数",
> "modelField.operation":"搜索条件",
> "orderByDesc":"modelField",
> "searchKeyword": "关键词"
> }
> ```
> 示例
> ```json
> {
> "current":1,
> "size":10,
> "userName.like":"github",
> "orderStatus.in":[1,3,4],
> "createTime.gt":1581392098000,
> "phone.isNotNull": "",
> "orderByDesc": "createTime",
> "searchKeyword": "githu"
> }
> ```
> 查询条件关键字
| KEYWORD | DESC |
| :-----------: | :----------------------------------------------------------: |
| modelField | 模型字段 |
| . | 分隔符 |
| operation | 不传 .operation,则默认为 eq。如果是模糊查询,支持字段加注解 @Like |
| orderByDesc | 递减 |
| orderByAsc | 递增 |
| searchKeyword | 关键词查询字段,搜索字段需要加上 @Keyword |
>operation 关键字列表
| operation | DESC | 语义 |
| :-------: | :---------------: | :-------------: |
| eq | 等于 | = |
| ne | 不等于 | <> |
| gt | 大于 | > |
| ge | 大于等于 | >= |
| lt | 小于 | < |
| le | 小于等于 | <= |
| like | 模糊匹配 | '%value%' |
| likeLeft | 以 value 结尾匹配 | '%value' |
| likeRight | 以 value 开头匹配 | 'value%' |
| in | in | in |
| between | 闭区间 | between s and e |
| isNull | is null | 为空 |
| isNotNull | is not null | 非空 |
#### 方法调用树示例
> ```json
> {
> "id":0,
> "parentId":-1,
> "lineNum":80,
> "method":"UserAuthController.register",
> "event":"小程序用户注册",
> "lifeCycle":"process",
> "sync":true,
> "childList":[
> {
> "id":7,
> "parentId":0,
> "lineNum":46,
> "method":"WechatServiceImpl.getUserInfoByCode",
> "event":"根据jscode获取用户信息",
> "lifeCycle":"phase",
> "sync":true
> },
> {
> "id":8,
> "parentId":0,
> "lineNum":25,
> "method":"BaseUserServiceImpl.getUserByOpenId",
> "event":"根据openId获取用户信息",
> "lifeCycle":"phase",
> "sync":true
> },
> {
> "id":9,
> "parentId":0,
> "lineNum":115,
> "method":"WechatAuthServiceImpl.invalidUserToken",
> "event":"失效用户token",
> "lifeCycle":"phase",
> "sync":true
> },
> {
> "id":10,
> "parentId":0,
> "lineNum":43,
> "method":"WechatAuthServiceImpl.register",
> "event":"注册用户到DMT系统",
> "lifeCycle":"phase",
> "sync":true,
> "childList":[
> {
> "id":11,
> "parentId":10,
> "lineNum":27,
> "method":"BaseGenerateCodeServiceImpl.generateNextCode",
> "event":"生成用户编码",
> "lifeCycle":"step",
> "sync":true
> }
> ]
> },
> {
> "id":12,
> "parentId":0,
> "lineNum":27,
> "method":"BaseUserTokenServiceImpl.saveNewToken",
> "event":"保存新token",
> "lifeCycle":"phase",
> "sync":true
> }
> ]
> }
> ```
>
>
>
>
================================================
FILE: _docs/fish_mysql.sql
================================================
/*
Navicat Premium Data Transfer
Target Server Type : MySQL
Target Server Version : 50628
File Encoding : 65001
Date: 27/07/2020 22:22:43
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for base_generate_code
-- ----------------------------
DROP TABLE IF EXISTS `base_generate_code`;
CREATE TABLE `base_generate_code` (
`id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '名称',
`target_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '目标',
`strategy` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生成策略:自增策略auto_increment',
`prefix` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '前缀',
`suffix` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '后缀',
`current_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生成的下一个号码',
`start_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '开始的号码',
`max_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最大的值',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`version` int(11) NOT NULL DEFAULT 1 COMMENT '版本'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '编码生成规则' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of base_generate_code
-- ----------------------------
INSERT INTO `base_generate_code` VALUES ('1', '大套餐-活动配置编码', 'BIG-PACKAGE-ACTIVITY-CONFIG', 'com.mcoding.modular.generatecode.strategy.DateIncrementStrategy', NULL, NULL, '202007210000001', '2020020900001', '9999999', '2020-02-09 12:39:47', '2020-02-09 12:39:51', 157);
INSERT INTO `base_generate_code` VALUES ('2', '大套餐-活动订单编码', 'BIG-PACKAGE-ACTIVITY-ORDER', 'com.mcoding.modular.generatecode.strategy.DateIncrementStrategy', NULL, NULL, '202007270000001', '2020020900001', '9999999', '2020-02-09 12:39:47', '2020-02-09 12:39:51', 317);
-- ----------------------------
-- Table structure for base_user
-- ----------------------------
DROP TABLE IF EXISTS `base_user`;
CREATE TABLE `base_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`openId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'openId',
`unionId` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'unionId',
`mobile_number` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号码',
`nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '昵称',
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名称',
`user_status` int(11) NOT NULL DEFAULT 0 COMMENT '用户状态,1为正常,2为冻结',
`avatar_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像',
`gender` int(11) NULL DEFAULT NULL COMMENT '性别 0:未知、1:男、2:女',
`province` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '省份',
`city` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '城市',
`country` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '区域',
`dealer_id` int(11) NULL DEFAULT NULL COMMENT '经销商ID',
`dealer_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '经销商编码',
`dealer_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '经销商名称',
`store_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '门店ID',
`store_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '门店编码',
`store_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '门店名称',
`binding_time` datetime(0) NULL DEFAULT NULL COMMENT '绑定时间',
`binding_status` int(11) NULL DEFAULT 0 COMMENT '绑定状态,0为未绑定,1为已绑定',
`order_quantity` int(11) NULL DEFAULT 0 COMMENT '产生订单数',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
`version` int(11) NOT NULL DEFAULT 1 COMMENT '版本',
`deleted` int(11) NOT NULL DEFAULT 0 COMMENT '是否删除,0为否,1为是',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '基础用户' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of base_user
-- ----------------------------
INSERT INTO `base_user` VALUES (2, 'oLrji5KPgx-4V_WFMcqX5D3c8fgs', NULL, '13800000001', '東', NULL, 0, 'https://wx.qlogo.cn/mmopen/vi_32/v6pQJTnBcricplYxWPgpKhWBGTFLSQTxmKzN9ADFdB8IFDPXRvGQzB2ccutEj3Bt7qyVUpe08b7Aqp8Skb2wUqw/132', 1, 'Guangdong', 'Guangzhou', 'China', 100278, 'JXS00071', '燕塘养猪场', '2c8f89d56c74fbfd016c752b7e5e0004', 'S0000011016', '西门烤鱼', NULL, 1, 0, '2020-04-15 10:49:18', '2020-07-23 14:47:10', 1, 0);
INSERT INTO `base_user` VALUES (3, 'ofind4vDz5-NVXMOUFcdW1jKi5bI', NULL, '13800000000', 'tomcatuw', NULL, 0, 'https://sdfsdf', 1, 'guangdong', 'guangzhou', 'tianhe', 195, 'lgx-000012', NULL, '', '', '', NULL, 0, 0, '2020-04-20 17:38:14', '2020-07-13 17:01:27', 1, 0);
-- ----------------------------
-- Table structure for base_user_token
-- ----------------------------
DROP TABLE IF EXISTS `base_user_token`;
CREATE TABLE `base_user_token` (
`user_id` int(11) NOT NULL COMMENT '用户ID',
`auth_token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '授权token',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
`version` int(11) NOT NULL DEFAULT 1 COMMENT '版本',
`deleted` int(11) NOT NULL DEFAULT 0 COMMENT '是否删除,0为否,1为是',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户授权token' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of base_user_token
-- ----------------------------
INSERT INTO `base_user_token` VALUES (2, '8krl9N2IzOWRkNmQ3ZGM5NGM1NDlhZmM4M2Y5YTc1NTI2N2E=', '2020-07-21 00:26:28', '2020-07-27 15:53:18', 1, 0);
INSERT INTO `base_user_token` VALUES (5, 'y953fZDc1NGU2ZmIyZDAxNGQyN2FjNDkxN2I4MTk5ZGQ0YWY=', '2020-07-21 00:31:36', '2020-07-27 18:04:32', 1, 0);
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '头像',
`account` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '账号',
`password` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
`salt` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'md5密码盐',
`name` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名字',
`birthday` datetime(0) NULL DEFAULT NULL COMMENT '生日',
`sex` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别(字典)',
`email` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电子邮件',
`phone` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电话',
`role_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色id(多个逗号隔开)',
`dept_id` bigint(20) NULL DEFAULT NULL COMMENT '部门id(多个逗号隔开)',
`status` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '状态(字典)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_user` bigint(20) NULL DEFAULT NULL COMMENT '创建人',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_user` bigint(20) NULL DEFAULT NULL COMMENT '更新人',
`version` int(11) NULL DEFAULT NULL COMMENT '乐观锁',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '管理员表' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, '1124606971782160385', 'admin', '123456', 'abcdef', 'admin', '2018-11-16 00:00:00', 'M', 'sn93@qq.com', '18200000000', '1', 25, 'ENABLE', '2016-01-29 08:49:53', NULL, '2019-06-28 14:38:19', 24, 25);
SET FOREIGN_KEY_CHECKS = 1;
================================================
FILE: applet/pom.xml
================================================
com.mcoding
tropical_fish
0.0.1-SNAPSHOT
4.0.0
applet
${parent.version}
jar
applet
UTF-8
UTF-8
1.8
com.mcoding
base-core
com.mcoding
biz-component
com.mcoding
biz-user
org.springframework.boot
spring-boot-starter-test
test
applet
org.springframework.boot
spring-boot-maven-plugin
================================================
FILE: applet/src/main/java/com/mcoding/applet/AppletApplication.java
================================================
package com.mcoding.applet;
import org.redisson.spring.session.config.EnableRedissonHttpSession;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
@EnableRedissonHttpSession
@EnableCaching
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.mcoding"})
public class AppletApplication {
public static void main(String[] args) {
SpringApplication.run(AppletApplication.class, args);
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/AppAuthController.java
================================================
package com.mcoding.applet.auth;
import com.alibaba.fastjson.JSON;
import com.mcoding.applet.auth.dto.CreateUserDto;
import com.mcoding.applet.auth.dto.RegisterDto;
import com.mcoding.base.core.doc.Process;
import com.mcoding.base.core.rest.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
*
* 基础用户
*
*
* @author wzt
* @since 2020-03-25
*/
@Slf4j
@Api(tags = "业务基础-APP授权服务")
@RestController
public class AppAuthController {
@Process(comment = "IOS用户注册")
@ApiOperation("IOS用户注册")
@PostMapping("/service/app/appuser/register")
public ResponseResult register(@Valid @RequestBody CreateUserDto createUserDto) {
log.info("EVENT=小程序用户注册|USER_INFO={}", JSON.toJSONString(createUserDto));
return ResponseResult.success(new RegisterDto());
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/WechatAuthController.java
================================================
package com.mcoding.applet.auth;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.mcoding.applet.auth.business.RegisterBo;
import com.mcoding.applet.auth.business.UserInfoBo;
import com.mcoding.applet.auth.service.BaseUserTokenService;
import com.mcoding.applet.auth.service.WechatAuthService;
import com.mcoding.applet.auth.manager.WechatClient;
import com.mcoding.applet.auth.util.LoginUserUtils;
import com.mcoding.applet.auth.dto.CreateUserDto;
import com.mcoding.applet.auth.dto.PhoneNumberDto;
import com.mcoding.applet.auth.dto.RegisterDto;
import com.mcoding.base.common.util.bean.BeanMapperUtils;
import com.mcoding.base.common.util.wechat.WXBizDataCrypt;
import com.mcoding.base.common.util.wechat.WxUserInfo;
import com.mcoding.base.core.doc.Process;
import com.mcoding.base.core.rest.ResponseCode;
import com.mcoding.base.core.rest.ResponseResult;
import com.mcoding.base.user.entity.BaseUser;
import com.mcoding.base.user.service.BaseUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
*
* 基础用户
*
*
* @author wzt
* @since 2020-03-25
*/
@Slf4j
@Api(tags = "业务基础-微信授权服务")
@RestController
public class WechatAuthController {
@Resource
private WechatClient wechatClient;
@Resource
private WechatAuthService wechatAuthService;
@Resource
private BaseUserService baseUserService;
@Resource
private BaseUserTokenService baseUserTokenService;
@ApiOperation("小程序获取用户手机号码")
@GetMapping("/service/app/wxuser/getPhoneNumber")
public ResponseResult getPhoneNumber(@ApiParam("加密数据") @RequestParam String encryptedData,
@ApiParam("加密算法的初始向量") @RequestParam String iv) {
String sessionKey = LoginUserUtils.getSessionKey();
log.info("EVENT=小程序获取用户手机号码|encryptedData={}|iv={}|sessionKey={}", encryptedData, iv, sessionKey);
WxUserInfo userInfo = WXBizDataCrypt.decrypt(encryptedData, sessionKey, iv);
String phoneNumber = userInfo.getPhoneNumber();
return ResponseResult.success(new PhoneNumberDto(phoneNumber));
}
@Process(comment = "小程序用户注册")
@ApiOperation("小程序用户注册")
@PostMapping("/service/app/wxuser/register")
public ResponseResult register(@Valid @RequestBody CreateUserDto createUserDto) {
log.info("EVENT=小程序用户注册|USER_INFO={}", JSON.toJSONString(createUserDto));
String jsCode = createUserDto.getJsCode();
UserInfoBo userInfoBo = this.wechatClient.getUserInfoByCode(jsCode);
String openId = userInfoBo.getOpenId();
BaseUser persistenceUser = this.baseUserService.getUserByOpenId(openId);
if (persistenceUser != null) {
String authToken = this.baseUserTokenService.getToken(persistenceUser.getId());
// 当前用户已有授权token,则失效该token
if (StringUtils.isNotEmpty(authToken)) {
this.wechatAuthService.invalidUserToken(authToken);
}
}
String newToken = IdUtil.simpleUUID();
RegisterBo registerBo = this.wechatAuthService.register(persistenceUser, createUserDto, userInfoBo, newToken);
// 记录当前用户Token
this.baseUserTokenService.saveNewToken(registerBo.getUserId(), newToken);
return ResponseResult.success(BeanMapperUtils.map(registerBo, RegisterDto.class));
}
@Process(comment = "小程序用户登录")
@ApiOperation("小程序用户登录")
@PostMapping("/service/app/wxuser/loginByJsCode")
public ResponseResult loginByJsCode(@ApiParam("JsCode") @RequestParam String jsCode) {
log.info("EVENT=小程序用户登录|JS_CODE={}", jsCode);
UserInfoBo userInfoBo = this.wechatClient.getUserInfoByCode(jsCode);
String openId = userInfoBo.getOpenId();
BaseUser persistenceUser = this.baseUserService.getUserByOpenId(openId);
if (persistenceUser == null) {
return ResponseResult.fail(ResponseCode.User_Not_Found, "用户还未注册,请先注册");
}
String authToken = this.baseUserTokenService.getToken(persistenceUser.getId());
// 当前用户已有授权token,则失效该token
if (StringUtils.isNotEmpty(authToken)) {
this.wechatAuthService.invalidUserToken(authToken);
}
String newToken = IdUtil.simpleUUID();
RegisterBo registerBo = this.wechatAuthService.login(persistenceUser, userInfoBo, newToken);
// 记录当前用户Token
this.baseUserTokenService.saveNewToken(registerBo.getUserId(), newToken);
return ResponseResult.success(BeanMapperUtils.map(registerBo, RegisterDto.class));
}
@ApiOperation("获取测试token[后端测试使用]")
@PostMapping("/service/app/wxuser/login")
public ResponseResult login(@RequestParam("openId") String openId) {
String token = IdUtil.simpleUUID();
RegisterBo registerBo = this.wechatAuthService.login(openId, token);
return ResponseResult.success(BeanMapperUtils.map(registerBo, RegisterDto.class));
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/base/LoginRequired.java
================================================
package com.mcoding.applet.auth.base;
import java.lang.annotation.*;
/**
* @author wzt on 2020/6/13.
* @version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface LoginRequired {
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/base/LoginRequiredArgumentResolver.java
================================================
package com.mcoding.applet.auth.base;
import com.mcoding.applet.auth.util.LoginUserUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* @author wzt on 2020/6/13.
* @version 1.0
*/
@Slf4j
public class LoginRequiredArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//匹配参数上具有@LoginRequired注解的参数
return methodParameter.hasParameterAnnotation(LoginRequired.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) {
return LoginUserUtils.getRegisterBo();
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/base/config/AuthConfig.java
================================================
package com.mcoding.applet.auth.base.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author wzt on 2019/11/13.
* @version 1.0
*/
@Configuration
public class AuthConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
//排除拦截
.excludePathPatterns("/service/app/wxuser/login")
.excludePathPatterns("/service/app/wxuser/register")
.excludePathPatterns("/service/app/wxuser/loginByJsCode")
.excludePathPatterns("/druid/**")
.excludePathPatterns("/javasimon/**")
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**")
.addPathPatterns("/**");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/templates/**.js").addResourceLocations("classpath:/templates/");
registry.addResourceHandler("/templates/**.css").addResourceLocations("classpath:/templates/");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/base/config/AuthInterceptor.java
================================================
package com.mcoding.applet.auth.base.config;
import com.alibaba.fastjson.JSON;
import com.mcoding.applet.auth.business.RegisterBo;
import com.mcoding.applet.auth.service.WechatAuthService;
import com.mcoding.applet.auth.util.LoginUserUtils;
import com.mcoding.base.core.rest.ResponseCode;
import com.mcoding.base.core.rest.ResponseResult;
import com.mcoding.base.core.spring.SpringContextHolder;
import groovy.util.logging.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* @author wzt on 2019/11/13.
* @version 1.0
*/
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if (StringUtils.isBlank(token)) {
this.write(response, ResponseResult.fail(ResponseCode.UNAUTHORIZED, "HTTP HEADER 携带的token不能为空"));
return false;
}
WechatAuthService wechatAuthService = SpringContextHolder.getOneBean(WechatAuthService.class);
RegisterBo registerBo = wechatAuthService.getUserToken(token);
if (registerBo == null) {
this.write(response, ResponseResult.fail(ResponseCode.UNAUTHORIZED, "token已失效,请重新授权登录认证"));
return false;
}
// 绑定当前线程对应的用户
LoginUserUtils.binding(registerBo);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 情况当前线程的绑定用户信息
LoginUserUtils.remove();
}
private void write(HttpServletResponse response, ResponseResult responseResult) throws Exception {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(responseResult));
writer.flush();
writer.close();
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/base/config/LoginRequiredConfig.java
================================================
package com.mcoding.applet.auth.base.config;
import com.mcoding.applet.auth.base.LoginRequiredArgumentResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author wzt on 2020/6/13.
* @version 1.0
*/
@Component
public class LoginRequiredConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List resolvers) {
resolvers.add(new LoginRequiredArgumentResolver());
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/business/RegisterBo.java
================================================
package com.mcoding.applet.auth.business;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author wzt on 2020/3/25.
* @version 1.0
*/
@ApiModel("注册用户信息")
@Data
public class RegisterBo implements Serializable {
@ApiModelProperty("服务端分配token")
private String token;
@ApiModelProperty("sessionKey")
private String sessionKey;
@ApiModelProperty("用户ID")
private Integer userId;
@ApiModelProperty("用户昵称")
private String nickName;
@ApiModelProperty("门店ID")
private String storeId;
@ApiModelProperty("门店编码")
private String storeCode;
@ApiModelProperty("门店名称")
private String storeName;
@ApiModelProperty("经销商ID")
private Integer dealerId;
@ApiModelProperty("经销商编码")
private String dealerCode;
@ApiModelProperty("经销商名称")
private String dealerName;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/business/UserInfoBo.java
================================================
package com.mcoding.applet.auth.business;
import lombok.Data;
/**
* @author wzt on 2019/11/12.
* @version 1.0
*/
@Data
public class UserInfoBo {
private String sessionKey;
private String openId;
private String unionid;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/business/resp/AccessTokenRespEntity.java
================================================
package com.mcoding.applet.auth.business.resp;
import lombok.Data;
/**
* @author wzt on 2020/3/25.
* @version 1.0
*/
@Data
public class AccessTokenRespEntity {
private String access_token;
private Long expires_in;
private String errcode;
private String errmsg;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/business/resp/JsCode2SessionRespEntity.java
================================================
package com.mcoding.applet.auth.business.resp;
import lombok.Data;
/**
* @author wzt on 2020/3/25.
* @version 1.0
*/
@Data
public class JsCode2SessionRespEntity {
private String session_key;
private String openid;
private String unionid;
private String errcode;
private String errmsg;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/business/resp/WxCodeUnlimitedResponse.java
================================================
package com.mcoding.applet.auth.business.resp;
import lombok.Data;
/**
* @author wzt on 2020/3/26.
* @version 1.0
*/
@Data
public class WxCodeUnlimitedResponse {
/**
* 请求失败错误码
*/
private String errcode;
/**
* 请求失败错误信息
*/
private String errmsg;
/**
* 图片信息
*/
private byte[] buffer;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/dao/BaseUserTokenMapper.java
================================================
package com.mcoding.applet.auth.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mcoding.applet.auth.entity.BaseUserToken;
/**
*
* 用户授权token Mapper 接口
*
*
* @author wzt
* @since 2020-04-20
*/
public interface BaseUserTokenMapper extends BaseMapper {
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/dto/BindingStoreDto.java
================================================
package com.mcoding.applet.auth.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author wzt on 2020/3/30.
* @version 1.0
*/
@Data
public class BindingStoreDto {
@ApiModelProperty("门店ID")
private String storeId;
@ApiModelProperty("门店编码")
private String storeCode;
@ApiModelProperty("门店名称")
private String storeName;
@ApiModelProperty("联系人")
private String contactMan;
@ApiModelProperty("手机号码")
private String mobileNumber;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/dto/CreateUserDto.java
================================================
package com.mcoding.applet.auth.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @author wzt on 2020/3/25.
* @version 1.0
*/
@Data
public class CreateUserDto {
@ApiModelProperty(value = "微信jscode")
@NotNull(message = "微信jscode不能为空")
private String jsCode;
@ApiModelProperty(value = "经销商ID")
@NotNull(message = "经销商ID不能为空")
private String dealerId;
@ApiModelProperty(value = "经销商编码")
@NotNull(message = "经销商编码不能为空")
private String dealerCode;
@ApiModelProperty(value = "经销商名称")
private String dealerName;
@ApiModelProperty(value = "手机号码")
private String mobileNumber;
@NotNull(message = "昵称不能为空")
@ApiModelProperty(value = "昵称")
private String nickName;
@ApiModelProperty(value = "头像")
private String avatarUrl;
@ApiModelProperty(value = "性别 0:未知、1:男、2:女")
private Integer gender;
@ApiModelProperty(value = "省份")
private String province;
@ApiModelProperty(value = "城市")
private String city;
@ApiModelProperty(value = "区域")
private String country;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/dto/PhoneNumberDto.java
================================================
package com.mcoding.applet.auth.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author wzt on 2020/4/15.
* @version 1.0
*/
@AllArgsConstructor
@Data
public class PhoneNumberDto {
private String phoneNumber;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/dto/RegisterDto.java
================================================
package com.mcoding.applet.auth.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author wzt on 2020/3/25.
* @version 1.0
*/
@ApiModel("注册用户信息")
@Data
public class RegisterDto implements Serializable {
@ApiModelProperty("服务端分配token")
private String token;
@ApiModelProperty("用户ID")
private Integer userId;
@ApiModelProperty("用户昵称")
private String nickName;
@ApiModelProperty("门店ID")
private String storeId;
@ApiModelProperty("门店编码")
private String storeCode;
@ApiModelProperty("门店名称")
private String storeName;
@ApiModelProperty("经销商ID")
private Integer dealerId;
@ApiModelProperty("经销商编码")
private String dealerCode;
@ApiModelProperty("经销商名称")
private String dealerName;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/entity/BaseUserToken.java
================================================
package com.mcoding.applet.auth.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
*
* 用户授权token
*
*
* @author wzt
* @since 2020-04-20
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("base_user_token")
@ApiModel(value="BaseUserToken", description="用户授权token")
public class BaseUserToken implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户ID")
@TableId("user_id")
private Integer userId;
@ApiModelProperty(value = "授权token")
@TableField("auth_token")
private String authToken;
@ApiModelProperty(value = "创建时间")
@TableField("create_time")
private Date createTime;
@ApiModelProperty(value = "更新时间")
@TableField("update_time")
private Date updateTime;
@ApiModelProperty(value = "版本")
@TableField("version")
private Integer version;
@ApiModelProperty(value = "是否删除,0为否,1为是")
@TableField("deleted")
private Integer deleted;
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/manager/WechatClient.java
================================================
package com.mcoding.applet.auth.manager;
import com.mcoding.applet.auth.business.UserInfoBo;
/**
* @author wzt on 2019/11/12.
* @version 1.0
*/
public interface WechatClient {
/**
* 根据jsCode获取用户信息
*
* @param code
* @return
*/
UserInfoBo getUserInfoByCode(String code);
/**
* 获取小程序全局唯一后台接口调用凭据
*
* @return
*/
String getAccessToken();
/**
* 失效小程序全局唯一后台接口调用凭据
*
* @return
*/
String evictAccessToken();
/**
* 获取小程序二维码
*
* @param accessToken
* @param page
* @param scene
* @param width
* @return
*/
byte[] getwxacode(String accessToken, String page, String scene, int width);
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/manager/impl/WechatClientImpl.java
================================================
package com.mcoding.applet.auth.manager.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.mcoding.applet.auth.business.UserInfoBo;
import com.mcoding.applet.auth.business.resp.AccessTokenRespEntity;
import com.mcoding.applet.auth.business.resp.JsCode2SessionRespEntity;
import com.mcoding.applet.auth.manager.WechatClient;
import com.mcoding.base.common.exception.CommonException;
import com.mcoding.base.core.cache.RCacheEvict;
import com.mcoding.base.core.cache.RCacheable;
import com.mcoding.base.core.doc.Phase;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author wzt on 2019/11/12.
* @version 1.0
*/
@Slf4j
@Service
public class WechatClientImpl implements WechatClient {
@Resource
private RestTemplate restTemplate;
@Value("${wechat.appid}")
private String appID;
@Value("${wechat.secret}")
private String appSecret;
@Phase(comment = "根据jscode获取用户信息")
@Override
public UserInfoBo getUserInfoByCode(String code) {
String requestUrl = this.buildJscode2sessionUrl(code);
ResponseEntity responseEntity = restTemplate.getForEntity(requestUrl, String.class);
log.info("EVENT=根据code获取用户信息|request_url={}|response_result={}", requestUrl, responseEntity);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
JsCode2SessionRespEntity body = JSON.parseObject(responseEntity.getBody(), JsCode2SessionRespEntity.class);
if (StringUtils.isNotBlank(body.getSession_key())) {
UserInfoBo userInfoBo = new UserInfoBo();
userInfoBo.setSessionKey(body.getSession_key());
userInfoBo.setOpenId(body.getOpenid());
userInfoBo.setUnionid(body.getUnionid());
return userInfoBo;
}
throw new CommonException(body.getErrmsg());
}
throw new CommonException("调用微信接口异常");
}
@Phase(comment = "获取小程序access_token")
@RCacheable(key = "dmt::wechat::global::AccessToken", ttl = 7000, timeUnit = TimeUnit.SECONDS,
resetTTL = false, serial = true)
@Override
public String getAccessToken() {
String requestUrl = this.buildAccessTokenUrl();
ResponseEntity responseEntity = this.restTemplate.getForEntity(requestUrl, String.class);
log.info("EVENT=获取微信小程序access_token|request_url={}|response_result={}", requestUrl, responseEntity);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
AccessTokenRespEntity body = JSON.parseObject(responseEntity.getBody(), AccessTokenRespEntity.class);
return body.getAccess_token();
}
throw new CommonException("调用微信接口异常");
}
@RCacheEvict(key = "dmt::wechat::global::AccessToken")
@Override
public String evictAccessToken() {
return null;
}
@Phase(comment = "调用微信服务,生成二维码字节流")
@Override
public byte[] getwxacode(String accessToken, String page, String scene, int width) {
String requestUrl = this.buildGetwxacodeUrl(accessToken);
Map params = Maps.newHashMap();
params.put("scene", scene);
params.put("width", width);
if (StringUtils.isNotBlank(page)) {
params.put("page", page);
}
log.info("EVENT=调用微信服务,生成二维码字节流|REQUEST_PARAM={}", JSON.toJSONString(params));
byte[] byteArray = null;
ResponseEntity entity = restTemplate.postForEntity(requestUrl, JSON.toJSONString(params), byte[].class);
// 图片或错误信息
byteArray = entity.getBody();
String wxReturnStr = new String(byteArray);
if (wxReturnStr.indexOf("errcode") != -1) {
JSONObject json = JSONObject.parseObject(wxReturnStr);
String errcode = json.get("errcode").toString();
String errmsg = json.get("errmsg").toString();
throw new CommonException(errcode + errmsg);
}
return byteArray;
}
private String buildGetwxacodeUrl(String accessToken) {
return String.format("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s", accessToken);
}
private String buildAccessTokenUrl() {
return String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
appID, appSecret);
}
private String buildJscode2sessionUrl(String jsCode) {
return String.format("https://api.weixin.qq.com/sns/jscode2session?appid=%1$s&secret=%2$s&js_code=%3$s&grant_type=authorization_code",
appID, appSecret, jsCode);
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/service/BaseUserTokenService.java
================================================
package com.mcoding.applet.auth.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mcoding.applet.auth.entity.BaseUserToken;
/**
*
* 用户授权token 服务类
*
*
* @author wzt
* @since 2020-04-20
*/
public interface BaseUserTokenService extends IService {
/**
* 保存新token
* @param userId
* @param newToken
*/
void saveNewToken(int userId, String newToken);
/**
* 查询token
* @param userId
* @return
*/
String getToken(int userId);
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/service/WechatAuthService.java
================================================
package com.mcoding.applet.auth.service;
import com.mcoding.applet.auth.business.RegisterBo;
import com.mcoding.applet.auth.business.UserInfoBo;
import com.mcoding.applet.auth.dto.CreateUserDto;
import com.mcoding.base.user.entity.BaseUser;
/**
* @author wzt on 2020/3/25.
* @version 1.0
*/
public interface WechatAuthService {
/**
* 创建用户
*
* @param createUserDto
* @param userInfoBo
* @param token
*/
RegisterBo register(BaseUser currentUser, CreateUserDto createUserDto, UserInfoBo userInfoBo, String token);
/**
* 登录
*
* @param persistenceUser
* @param userInfoBo
* @param token
* @return
*/
RegisterBo login(BaseUser persistenceUser, UserInfoBo userInfoBo, String token);
/**
* 根据openId登录
*
* @param openId
*/
RegisterBo login(String openId, String token);
/**
* 查询当前用户token
*
* @param token
* @return
*/
RegisterBo getUserToken(String token);
/**
* 失效用户token
*
* @param token
*/
void invalidUserToken(String token);
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/service/impl/BaseUserTokenServiceImpl.java
================================================
package com.mcoding.applet.auth.service.impl;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mcoding.applet.auth.dao.BaseUserTokenMapper;
import com.mcoding.applet.auth.entity.BaseUserToken;
import com.mcoding.applet.auth.service.BaseUserTokenService;
import com.mcoding.base.core.doc.Phase;
import jodd.util.Base64;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Optional;
/**
*
* 用户授权token 服务实现类
*
*
* @author wzt
* @since 2020-04-20
*/
@Slf4j
@Service
public class BaseUserTokenServiceImpl extends ServiceImpl implements BaseUserTokenService {
@Phase(comment = "保存新token")
@Override
public void saveNewToken(int userId, String newToken) {
BaseUserToken tokenEntity = new BaseUserToken();
tokenEntity.setUserId(userId);
String fake = RandomUtil.randomString(5);
String encryptToken = Base64.encodeToString(newToken);
tokenEntity.setAuthToken(fake + encryptToken);
BaseUserToken baseUserToken = this.getById(userId);
if (baseUserToken == null) {
tokenEntity.setCreateTime(new Date());
this.save(tokenEntity);
} else {
this.updateById(tokenEntity);
}
}
@Override
public String getToken(int userId) {
BaseUserToken baseUserToken = this.getById(userId);
String encryptToken = Optional.ofNullable(baseUserToken).map(BaseUserToken::getAuthToken).orElse("");
if (StringUtils.isEmpty(encryptToken)) {
return null;
}
String base64Token = StrUtil.sub(encryptToken, 5, encryptToken.length());
return Base64.decodeToString(base64Token);
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/service/impl/WechatAuthServiceImpl.java
================================================
package com.mcoding.applet.auth.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mcoding.applet.auth.business.RegisterBo;
import com.mcoding.applet.auth.business.UserInfoBo;
import com.mcoding.applet.auth.dto.CreateUserDto;
import com.mcoding.applet.auth.service.WechatAuthService;
import com.mcoding.base.common.util.bean.BeanMapperUtils;
import com.mcoding.base.core.cache.RCacheEvict;
import com.mcoding.base.core.cache.RCacheable;
import com.mcoding.base.core.doc.Phase;
import com.mcoding.base.user.entity.BaseUser;
import com.mcoding.base.user.service.BaseUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 小程序用户服务
*
* @author wzt on 2020/3/25.
* @version 1.0
*/
@Service
public class WechatAuthServiceImpl implements WechatAuthService {
@Resource
private BaseUserService baseUserService;
@Phase(comment = "注册用户到DMT系统")
@RCacheable(key = "dmt::miniprogram::token", secKey = "#token", ttl = 1, timeUnit = TimeUnit.DAYS)
@Override
public RegisterBo register(BaseUser persistenceUser, CreateUserDto createUserDto, UserInfoBo userInfoBo, String token) {
RegisterBo registerBo;
if (persistenceUser == null) {
// 当前用户还未入库,则先入库
BaseUser baseUser = BeanMapperUtils.map(createUserDto, BaseUser.class);
baseUser.setOpenId(userInfoBo.getOpenId());
baseUser.setUnionId(userInfoBo.getUnionid());
baseUser.setCreateTime(new Date());
baseUserService.save(baseUser);
registerBo = BeanMapperUtils.map(baseUser, RegisterBo.class);
registerBo.setUserId(baseUser.getId());
registerBo.setSessionKey(userInfoBo.getSessionKey());
registerBo.setToken(token);
} else {
registerBo = BeanMapperUtils.map(persistenceUser, RegisterBo.class);
registerBo.setUserId(persistenceUser.getId());
registerBo.setSessionKey(userInfoBo.getSessionKey());
registerBo.setToken(token);
}
return registerBo;
}
@Phase(comment = "用户登录DMT系统")
@RCacheable(key = "dmt::miniprogram::token", secKey = "#token", ttl = 1, timeUnit = TimeUnit.DAYS)
@Override
public RegisterBo login(BaseUser persistenceUser, UserInfoBo userInfoBo, String token) {
RegisterBo registerBo = BeanMapperUtils.map(persistenceUser, RegisterBo.class);
registerBo.setUserId(persistenceUser.getId());
registerBo.setSessionKey(userInfoBo.getSessionKey());
registerBo.setToken(token);
return registerBo;
}
@RCacheable(key = "dmt::miniprogram::token", secKey = "#token", ttl = 2, timeUnit = TimeUnit.HOURS)
@Override
public RegisterBo login(String openId, String token) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(BaseUser::getOpenId, openId);
BaseUser currentUser = baseUserService.getOne(queryWrapper);
RegisterBo registerBo = BeanMapperUtils.map(currentUser, RegisterBo.class);
registerBo.setUserId(currentUser.getId());
registerBo.setToken(token);
return registerBo;
}
@RCacheable(key = "dmt::miniprogram::token", secKey = "#token", resetTTL = false)
@Override
public RegisterBo getUserToken(String token) {
return null;
}
@Phase(comment = "失效用户token")
@RCacheEvict(key = "dmt::miniprogram::token", secKey = "#token")
@Override
public void invalidUserToken(String token) {
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/auth/util/LoginUserUtils.java
================================================
package com.mcoding.applet.auth.util;
import com.mcoding.applet.auth.business.RegisterBo;
/**
* @author wzt on 2019/11/13.
* @version 1.0
*/
public class LoginUserUtils {
private static ThreadLocal threadLocal = new ThreadLocal<>();
public static void binding(RegisterBo registerBo) {
threadLocal.set(registerBo);
}
public static RegisterBo getRegisterBo() {
return threadLocal.get();
}
public static Integer getUserId() {
return getRegisterBo().getUserId();
}
public static String getToken() {
return getRegisterBo().getToken();
}
public static String getSessionKey() {
return getRegisterBo().getSessionKey();
}
public static void remove() {
threadLocal.remove();
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/order/component/ActivityCodeController.java
================================================
package com.mcoding.applet.order.component;
import com.alibaba.fastjson.JSON;
import com.mcoding.base.core.rest.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.javasimon.aop.Monitored;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
;
/**
* @author wzt on 2020/2/9.
* @version 1.0
*/
@Slf4j
@Api(tags = "业务-活动订单服务")
@RestController
public class ActivityCodeController {
@Resource
private ActivityOrderBizCodeGenerator tradeOrderBizCodeGenerator;
@Monitored
@ApiOperation("生成订单活动编码")
@PostMapping("/service/activityorder/generateBizCode")
public ResponseResult generateBizCode(@RequestParam int quantity) {
List codeList = IntStream.rangeClosed(1, quantity)
.parallel()
.mapToObj(num -> tradeOrderBizCodeGenerator.generateNextCode())
.sorted()
.collect(Collectors.toList());
log.info("biz code List = {}", JSON.toJSONString(codeList));
return ResponseResult.success(codeList.size());
}
@Monitored
@ApiOperation("批量生成订单活动编码")
@PostMapping("/service/activityorder/generateBizCodeList")
public ResponseResult generateBizCodeList(@RequestParam int quantity) {
List codeList = this.tradeOrderBizCodeGenerator.generateBizCodeList(quantity);
log.info("biz code List = {}", JSON.toJSONString(codeList));
return ResponseResult.success(codeList.size());
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/order/component/ActivityOrderBizCodeGenerator.java
================================================
package com.mcoding.applet.order.component;
import com.google.common.collect.Range;
import com.mcoding.base.common.exception.BizException;
import com.mcoding.base.component.generatecode.domain.CommonBizCodeGenerator;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author wzt on 2020/6/26.
* @version 1.0
*/
@Component
public class ActivityOrderBizCodeGenerator extends CommonBizCodeGenerator {
public ActivityOrderBizCodeGenerator() {
this.setTargetCode(TargetCodeEnum.BIG_PACKAGE_ACTIVITY_ORDER.getTargetCode());
this.setCacheQuantity(100);
}
@Override
public String generateNextCode() {
return super.generateNextCode();
}
@Override
public List generateBizCodeList(int quantity) {
if (!Range.closed(1, 5000).contains(quantity)) {
throw new BizException("批量生成的数量必须在1 到 5000 之间");
}
return super.generateBizCodeList(quantity);
}
}
================================================
FILE: applet/src/main/java/com/mcoding/applet/order/component/TargetCodeEnum.java
================================================
package com.mcoding.applet.order.component;
/**
* @author wzt on 2020/2/9.
* @version 1.0
*/
public enum TargetCodeEnum {
BIG_PACKAGE_ACTIVITY_ORDER("BIG-PACKAGE-ACTIVITY-ORDER", "大套餐-活动订单编码");
private String targetCode;
private String desc;
TargetCodeEnum(String targetCode, String desc) {
this.targetCode = targetCode;
this.desc = desc;
}
public String getTargetCode() {
return targetCode;
}
public String getDesc() {
return desc;
}
}
================================================
FILE: applet/src/main/resources/application-dev.properties
================================================
spring.datasource.druid.url=jdbc:mysql://47.95.192.230:3306/mcoding?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.connection-init-sqls=set names utf8mb4
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=false
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.filters=stat,wall,slf4j
spring.datasource.druid.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.allow=
spring.datasource.druid.stat-view-servlet.deny=
spring.datasource.druid.stat-view-servlet.reset-enable=false
spring.datasource.druid.stat-view-servlet.login-username=druid
spring.datasource.druid.stat-view-servlet.login-password=druid#123456
spring.redis.redisson.config=classpath:prop/redisson-dev.yaml
spring.session.store-type=redis
spring.session.timeout.seconds=900
wechat.appid=wxbf9d7e5f7c669528
wechat.secret=1582d57e2b63ea23b564839d835ca314
elasticsearch.clusterNodes=127.0.0.1:9200
================================================
FILE: applet/src/main/resources/application-prd.properties
================================================
================================================
FILE: applet/src/main/resources/application.properties
================================================
spring.application.name=applet-api
server.port=8086
server.servlet.context-path=/applet-api
spring.profiles.active=dev
spring.mvc.servlet.load-on-startup=1
spring.main.allow-bean-definition-overriding=true
spring.jackson.serialization.write-dates-as-timestamps: true
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis-plus.configuration.log-impl=com.mcoding.base.core.log.MybatisLogImpl
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
================================================
FILE: applet/src/main/resources/logback-spring.xml
================================================
logback
debug
${CONSOLE_LOG_PATTERN}
UTF-8
${log.path}/log_debug.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
100MB
15
debug
ACCEPT
DENY
${log.path}/log_info.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
100MB
15
info
ACCEPT
DENY
${log.path}/log_warn.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
100MB
15
warn
ACCEPT
DENY
${log.path}/log_error.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
100MB
15
ERROR
ACCEPT
DENY
================================================
FILE: applet/src/main/resources/prop/redisson-dev.yaml
================================================
singleServerConfig:
address: "redis://47.95.192.230:6379"
password: redis#123
clientName: fish_api_dev
database: 0
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 3000
timeout: 5000
retryAttempts: 3
retryInterval: 1500
subscriptionsPerConnection: 5
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 8
connectionPoolSize: 16
dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec:
class: "org.redisson.codec.JsonJacksonCodec"
transportMode: "NIO"
================================================
FILE: backend/pom.xml
================================================
4.0.0
com.mcoding
tropical_fish
0.0.1-SNAPSHOT
backend
${parent.version}
jar
backend
UTF-8
UTF-8
1.8
com.mcoding
base-core
com.mcoding
biz-user
org.springframework.boot
spring-boot-starter-test
test
backend
org.springframework.boot
spring-boot-maven-plugin
================================================
FILE: backend/src/main/java/com/mcoding/BackendApplication.java
================================================
package com.mcoding;
import org.redisson.spring.session.config.EnableRedissonHttpSession;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
@EnableRedissonHttpSession
@EnableCaching
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.mcoding"})
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/config/AuthConfig.java
================================================
package com.mcoding.modular.auth.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author wzt on 2019/11/13.
* @version 1.0
*/
@Configuration
public class AuthConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
//排除拦截
.excludePathPatterns("/service/auth/login")
.excludePathPatterns("/noLogin/**")
.excludePathPatterns("/druid/**")
.excludePathPatterns("/javasimon/**")
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**")
.addPathPatterns("/**");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/templates/**.js").addResourceLocations("classpath:/templates/");
registry.addResourceHandler("/templates/**.css").addResourceLocations("classpath:/templates/");
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/config/AuthInterceptor.java
================================================
package com.mcoding.modular.auth.config;
import com.alibaba.fastjson.JSON;
import com.mcoding.base.core.rest.ResponseCode;
import com.mcoding.base.core.rest.ResponseResult;
import com.mcoding.modular.system.user.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* @author wzt on 2019/11/13.
* @version 1.0
*/
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
SysUser loginUser = (SysUser) request.getSession().getAttribute("currentUser");
if (loginUser == null) {
this.write(response, ResponseResult.fail(ResponseCode.UNAUTHORIZED, "请先登录"));
return false;
}
return true;
}
private void write(HttpServletResponse response, ResponseResult responseResult) throws Exception {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(responseResult));
writer.flush();
writer.close();
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/config/LoginRequiredConfig.java
================================================
package com.mcoding.modular.auth.config;
import com.mcoding.modular.auth.support.LoginRequiredArgumentResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author wzt on 2020/6/13.
* @version 1.0
*/
@Component
public class LoginRequiredConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List resolvers) {
resolvers.add(new LoginRequiredArgumentResolver());
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/controller/AppAuthController.java
================================================
package com.mcoding.modular.auth.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mcoding.base.common.util.Assert;
import com.mcoding.base.core.rest.ResponseResult;
import com.mcoding.modular.auth.support.LoginRequired;
import com.mcoding.modular.auth.support.LoginUserUtils;
import com.mcoding.modular.system.user.entity.SysUser;
import com.mcoding.modular.system.user.service.SysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.javasimon.aop.Monitored;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
/**
* @author wzt on 2020/6/19.
* @version 1.0
*/
@Api(tags = "业务-鉴权服务")
@RestController
public class AppAuthController {
@Resource
private SysUserService sysUserService;
@Monitored
@ApiOperation(value = "登录")
@PostMapping("/service/auth/login")
public ResponseResult login(@ApiParam("名称") @RequestParam(defaultValue = "admin") String name,
@ApiParam("密码") @RequestParam(defaultValue = "123456") String password) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(SysUser::getName, name)
.eq(SysUser::getPassword, password);
SysUser currentUser = this.sysUserService.getOne(queryWrapper);
Assert.isNotNull(currentUser, String.format("用户%s不存在", name));
LoginUserUtils.markAsLogin(currentUser);
return ResponseResult.success(currentUser);
}
@Monitored
@ApiOperation(value = "whoAmI")
@GetMapping("/service/auth/whoAmI")
public ResponseResult whoAmI(@ApiIgnore @LoginRequired SysUser sysUser) {
return ResponseResult.success(sysUser);
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/support/LoginRequired.java
================================================
package com.mcoding.modular.auth.support;
import java.lang.annotation.*;
/**
* @author wzt on 2020/6/13.
* @version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface LoginRequired {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/support/LoginRequiredArgumentResolver.java
================================================
package com.mcoding.modular.auth.support;
import com.mcoding.base.common.exception.CommonException;
import com.mcoding.modular.system.user.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* @author wzt on 2020/6/13.
* @version 1.0
*/
@Slf4j
public class LoginRequiredArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//匹配参数上具有@LoginRequired注解的参数
return methodParameter.hasParameterAnnotation(LoginRequired.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) {
SysUser sysUser = LoginUserUtils.currentUser();
if (sysUser == null) {
log.error("接口 {} 非法调用!", methodParameter.getMethod().toString());
throw new CommonException("请先登录!");
}
return sysUser;
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/auth/support/LoginUserUtils.java
================================================
package com.mcoding.modular.auth.support;
import com.mcoding.modular.system.user.entity.SysUser;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @author wzt on 2020/6/19.
* @version 1.0
*/
public class LoginUserUtils {
public static SysUser currentUser() {
HttpSession httpSession = getCurrentSession();
return (SysUser) httpSession.getAttribute("currentUser");
}
public static Long getUserId() {
return currentUser().getId();
}
public static void markAsLogin(SysUser loginUser) {
HttpSession httpSession = getCurrentSession();
httpSession.setAttribute("currentUser", loginUser);
}
public static void invalidate() {
HttpSession httpSession = getCurrentSession();
httpSession.invalidate();
}
public static void loginOut() {
HttpSession httpSession = getCurrentSession();
httpSession.invalidate();
}
private static HttpSession getCurrentSession() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
return request.getSession();
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/biz/user/controller/BizUserController.java
================================================
package com.mcoding.modular.biz.user.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Maps;
import com.itextpdf.kernel.geom.PageSize;
import com.mcoding.base.common.util.Assert;
import com.mcoding.base.common.util.pdf.FtlToPdfUtil;
import com.mcoding.base.core.orm.DslParser;
import com.mcoding.base.core.rest.ResponseResult;
import com.mcoding.base.user.entity.BaseUser;
import com.mcoding.base.user.service.BaseUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
*
* 业务用户
*
*
* @author wzt
* @since 2020-06-21
*/
@Slf4j
@Api(tags = "业务-用户服务")
@RestController
public class BizUserController {
@Resource
private BaseUserService baseUserService;
@ApiOperation("创建")
@PostMapping("/service/user/create")
public ResponseResult create(@Valid @RequestBody BaseUser baseUser) {
baseUserService.save(baseUser);
return ResponseResult.success();
}
@ApiOperation(value = "删除")
@PostMapping("/service/user/delete")
public ResponseResult delete(@RequestParam Integer id) {
baseUserService.removeById(id);
return ResponseResult.success();
}
@ApiOperation(value = "修改")
@PostMapping("/service/user/modify")
public ResponseResult modify(@Valid @RequestBody BaseUser baseUser) {
baseUserService.updateById(baseUser);
return ResponseResult.success();
}
@ApiOperation(value = "查询用户详情")
@GetMapping("/service/user/detail")
public ResponseResult detail(@RequestParam Integer id) {
return ResponseResult.success(baseUserService.getById(id));
}
@ApiOperation(value = "分页查询")
@PostMapping("/service/user/queryByPage")
public ResponseResult> queryByPage(@RequestBody JSONObject queryObject) {
DslParser dslParser = new DslParser<>(queryObject);
QueryWrapper queryWrapper = dslParser.parseToWrapper(BaseUser.class);
IPage page = dslParser.generatePage();
baseUserService.page(page, queryWrapper);
return ResponseResult.success(page);
}
@ApiOperation("导出模板")
@GetMapping(value = "/service/user/exportExcelTemplate")
@ResponseBody
public ResponseResult exportExcelTemplate(HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse.reset();
httpServletResponse.setContentType("application/vnd.ms-excel;charset=utf-8");
httpServletResponse.setHeader("Content-Disposition", String.format("attachment;filename=%s.xlsx",
new String("用户导入模板".getBytes("UTF-8"), "ISO8859-1")));
httpServletResponse.addHeader("Cache-Control", "no-cache");
InputStream templateIs = this.getFileFromClassPathResource("template/UserTemplate.xlsx");
OutputStream outputStream = httpServletResponse.getOutputStream();
ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(templateIs).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().build();
excelWriter.fill(new FillWrapper("user", Collections.emptyList()), fillConfig, writeSheet);
excelWriter.finish();
outputStream.close();
return null;
}
@ApiOperation("导入")
@PostMapping(value = "/service/user/importByExcel")
@ResponseBody
public ResponseResult importByExcel(@RequestParam("file") MultipartFile file) throws Exception {
List userList = EasyExcel.read(file.getInputStream(), BaseUser.class, new UserDataListener())
.sheet()
.doReadSync();
log.info("userList = {}", JSON.toJSONString(userList));
return ResponseResult.success();
}
@Resource
private RedissonClient redissonClient;
@ApiOperation("EXCEL导出,请求参数换取导出标识ID")
@PostMapping(value = "/service/user/exchangeExportExcelId")
@ResponseBody
public ResponseResult exchangeExportExcelId(@RequestBody JSONObject queryObject) {
String exportExcelId = UUID.randomUUID().toString(true);
redissonClient.getBucket("user::export::excel::" + exportExcelId)
.set(queryObject, 30, TimeUnit.SECONDS);
return ResponseResult.success(exportExcelId);
}
@ApiOperation("EXCEL导出")
@GetMapping(value = "/service/user/exportByExcel/{exportExcelId}")
@ResponseBody
public ResponseResult exportByExcel(
@ApiParam("导出excel标识") @PathVariable("exportExcelId") String exportExcelId,
HttpServletResponse httpServletResponse) throws Exception {
JSONObject jsonObject = (JSONObject) this.redissonClient.getBucket("user::export::excel::" + exportExcelId).get();
Assert.isNotNull(jsonObject, "导出excel标识ID已过期或者不不存在");
String fileName = "用户明细" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
httpServletResponse.reset();
httpServletResponse.setContentType("application/vnd.ms-excel;charset=utf-8");
httpServletResponse.setHeader("Content-Disposition", String.format("attachment;filename=%s.xls",
new String(fileName.getBytes("UTF-8"), "ISO8859-1")));
httpServletResponse.addHeader("Cache-Control", "no-cache");
InputStream templateIs = this.getFileFromClassPathResource("template/UserTemplate.xlsx");
OutputStream outputStream = httpServletResponse.getOutputStream();
ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(templateIs).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().build();
List userList = this.getUserList(jsonObject);
excelWriter.fill(new FillWrapper("user", userList), fillConfig, writeSheet);
excelWriter.finish();
outputStream.close();
return null;
}
@ApiOperation("PDF导出")
@GetMapping(value = "/service/user/exportByPdf/{exportExcelId}")
@ResponseBody
public void exportPdf(@ApiParam("导出excel标识") @PathVariable("exportExcelId") String exportPdfId,
HttpServletResponse httpServletResponse) throws Exception {
JSONObject jsonObject = (JSONObject) this.redissonClient.getBucket("user::export::excel::" + exportPdfId).get();
Assert.isNotNull(jsonObject, "导出excel标识ID已过期或者不不存在");
httpServletResponse.setContentType("application/x-msdownload");
String fileName = URLEncoder.encode("用户明细.pdf", "UTF-8");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName);
List userList = this.getUserList(jsonObject);
Map dataSource = Maps.newHashMap();
dataSource.put("userList", userList);
// 去读模板文件 -> 替换占位符 -> 生成 HTML 字节数组
byte[] htmlByteArray = FtlToPdfUtil.generateHtmlByteArray("/template", "UserTemplate.ftl", dataSource);
OutputStream outputStream = httpServletResponse.getOutputStream();
// HTML 转换为 PDF
FtlToPdfUtil.convertToPdf(htmlByteArray, outputStream, PageSize.A2);
outputStream.flush();
outputStream.close();
}
private List getUserList(JSONObject jsonObject) {
DslParser dslParser = new DslParser<>(jsonObject);
dslParser.parseToWrapper(BaseUser.class);
QueryWrapper queryWrapper = dslParser.getQueryWrapper();
queryWrapper.lambda().orderByDesc(BaseUser::getCreateTime);
return this.baseUserService.list(queryWrapper);
}
private InputStream getFileFromClassPathResource(String filePath) throws IOException {
ClassPathResource classPathResource = new ClassPathResource(filePath);
return classPathResource.getInputStream();
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/biz/user/controller/UserDataListener.java
================================================
package com.mcoding.modular.biz.user.controller;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.mcoding.base.user.entity.BaseUser;
import lombok.extern.slf4j.Slf4j;
/**
* Created on 2022/4/9.
*
* @author wzt
*/
@Slf4j
public class UserDataListener implements ReadListener {
@Override
public void invoke(BaseUser baseUser, AnalysisContext analysisContext) {
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/rule/ACmp.java
================================================
package com.mcoding.modular.rule;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
/**
* @author wzt
* @since 2022/11/30
*/
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("fuck A");
//do your business
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/rule/BCmp.java
================================================
package com.mcoding.modular.rule;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
/**
* @author wzt
* @since 2022/11/30
*/
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
//do your business
System.out.println("fuck B");
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/rule/BizFlow.java
================================================
package com.mcoding.modular.rule;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author wzt
* @since 2022/11/30
*/
@Slf4j
@Component
public class BizFlow {
@Resource
private FlowExecutor flowExecutor;
public void execute() {
flowExecutor.reloadRule();
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
DefaultContext context = response.getFirstContextBean();
System.out.println(JsonUtil.toJsonString(context.getData("student")));
if (response.isSuccess()){
log.info("执行成功");
}else{
log.info("执行失败");
}
String code = response.getCode();
String message = response.getMessage();
System.out.println(String.format("%s %s", code, message));
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/rule/CCmp.java
================================================
package com.mcoding.modular.rule;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
/**
* @author wzt
* @since 2022/11/30
*/
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
//do your business
System.out.println("fuck C");
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/rule/RuleFlowController.java
================================================
package com.mcoding.modular.rule;
import com.mcoding.base.core.rest.ResponseResult;
import com.mcoding.modular.system.user.entity.SysUser;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.util.JsonUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.javasimon.aop.Monitored;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author wzt on 2020/6/19.
* @version 1.0
*/
@Api(tags = "基础-规则引擎")
@RestController
public class RuleFlowController {
@Resource
private BizFlow bizFlow;
@Monitored
@ApiOperation(value = "执行")
@PostMapping("/noLogin/ruleFlow")
public ResponseResult execute() {
bizFlow.execute();
return ResponseResult.success();
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/search/controller/ProductSpuController.java
================================================
package com.mcoding.modular.search.controller;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mcoding.base.core.orm.DslParser;
import com.mcoding.base.core.rest.*;
import com.mcoding.modular.search.service.ProductSpuService;
import com.mcoding.modular.search.entity.ProductSpu;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import java.util.List;
/**
*
* 商品SPU
*
*
* @author wzt
* @since 2022-06-24
*/
@Slf4j
@Api(tags = "业务-商品服务")
@RestController
public class ProductSpuController {
@Resource
private ProductSpuService productSpuService;
@ApiOperation("创建")
@PostMapping("/service/goods/create")
public ResponseResult create(@Valid @RequestBody ProductSpu productSpu) {
productSpuService.save(productSpu);
return ResponseResult.success();
}
@ApiOperation(value = "删除")
@PostMapping("/service/goods/delete")
public ResponseResult delete(@RequestParam Integer id) {
productSpuService.removeById(id);
return ResponseResult.success();
}
@ApiOperation(value = "修改")
@PostMapping("/service/goods/modify")
public ResponseResult modify(@Valid @RequestBody ProductSpu productSpu) {
productSpuService.updateById(productSpu);
return ResponseResult.success();
}
@ApiOperation(value = "查询活动详情")
@GetMapping("/service/goods/detail")
public ResponseResult detail(@RequestParam Integer id) {
return ResponseResult.success(productSpuService.getById(id));
}
@ApiOperation(value = "分页查询")
@PostMapping("/service/goods/queryByPage")
public ResponseResult> queryByPage(@RequestBody JSONObject queryObject) {
DslParser dslParser = new DslParser<>(queryObject);
QueryWrapper queryWrapper = dslParser.parseToWrapper(ProductSpu.class);
IPage page = dslParser.generatePage();
productSpuService.page(page, queryWrapper);
return ResponseResult.success(page);
}
@Resource
private RestHighLevelClient restHighLevelClient;
private String indexName = "mall_product_spu";
@ApiOperation("全量同步商品索引")
@PostMapping("/service/goods/fullIndex")
public ResponseResult fullIndex() {
List productSpuList = this.productSpuService.list();
BulkRequest bulkRequest = new BulkRequest();
for (ProductSpu productSpu : productSpuList) {
IndexRequest indexRequest = new IndexRequest(indexName, "doc")
.id(productSpu.getId().toString())
.opType(DocWriteRequest.OpType.CREATE)
.source(JSONObject.toJSONString(productSpu), XContentType.JSON);
bulkRequest.add(indexRequest);
}
try {
BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
log.info("返回状态{}", bulkResponse.status());
if (bulkResponse.status() == RestStatus.OK) {
return ResponseResult.success();
}
} catch (IOException e) {
log.error("批量操作失败", e);
}
return ResponseResult.success();
}
@ApiOperation("修改商品文档数据")
@PostMapping("/service/goods/esUpdate")
public ResponseResult esUpdate(@RequestParam String spuId) {
ProductSpu productSpu = this.productSpuService.getById(spuId);
productSpu.setName(productSpu.getName() + "_update");
UpdateRequest updateRequest = new UpdateRequest(indexName, "doc", spuId);
updateRequest.doc(JSONObject.toJSONString(productSpu), XContentType.JSON);
updateRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
try {
UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
log.info("返回状态{}", updateResponse.status());
if (updateResponse.status() == RestStatus.OK) {
return ResponseResult.success();
}
} catch (IOException e) {
log.error("批量操作失败", e);
}
return ResponseResult.success();
}
@ApiOperation("删除商品文档数据")
@PostMapping("/service/goods/esDelete")
public ResponseResult esDelete(@RequestParam String spuId) {
DeleteRequest deleteRequest = new DeleteRequest(indexName, "doc", spuId);
try {
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
log.info("返回状态{}", deleteResponse.status());
if (deleteResponse.status() == RestStatus.OK) {
return ResponseResult.success();
}
} catch (IOException e) {
log.error("批量操作失败", e);
}
return ResponseResult.success();
}
@ApiOperation("分页检索商品")
@PostMapping("/service/goods/search")
public ResponseResult search(@RequestParam String content, @RequestParam int pageNo, @RequestParam int pageSize) {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.from((pageNo - 1) * pageSize);
sourceBuilder.size(pageSize);
sourceBuilder.sort("createdDate", SortOrder.DESC);
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("name", content))
.must(QueryBuilders.termsQuery("companyId", new int[]{1302, 1600}));
sourceBuilder.query(boolQuery);
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.source(sourceBuilder);
log.info("searchRequest content = {}", sourceBuilder);
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("返回状态{}", searchResponse.status());
if (searchResponse.status() == RestStatus.OK) {
SearchHits searchHits = searchResponse.getHits();
log.info("hits={}", searchHits.totalHits);
return ResponseResult.success();
}
} catch (IOException e) {
log.error("操作失败", e);
}
return ResponseResult.success();
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/search/dao/ProductSpuMapper.java
================================================
package com.mcoding.modular.search.dao;
import com.mcoding.modular.search.entity.ProductSpu;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
*
* 商品SPU Mapper 接口
*
*
* @author wzt
* @since 2022-06-24
*/
public interface ProductSpuMapper extends BaseMapper {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/search/entity/ProductSpu.java
================================================
package com.mcoding.modular.search.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
/**
*
* 商品SPU
*
*
* @author wzt
* @since 2022-06-24
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("product_spu")
@ApiModel(value="ProductSpu", description="商品SPU")
public class ProductSpu implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "编号")
@TableField("code")
private String code;
@ApiModelProperty(value = "名称")
@TableField("name")
private String name;
@ApiModelProperty(value = "副标题")
@TableField("sub_title")
private String subTitle;
@ApiModelProperty(value = "描述")
@TableField("description")
private String description;
@ApiModelProperty(value = "是否是多规")
@TableField("spec_type")
private Boolean specType;
@ApiModelProperty(value = "备注(后台用,前端暂不展示)")
@TableField("memo")
private String memo;
@ApiModelProperty(value = "pc详情")
@TableField("introduce_pc")
private String introducePc;
@ApiModelProperty(value = "移动详情")
@TableField("introduce_mobile")
private String introduceMobile;
@ApiModelProperty(value = "小程序详情")
@TableField("introduce_program")
private String introduceProgram;
@ApiModelProperty(value = "生产厂家")
@TableField("manufacturer_name")
private String manufacturerName;
@ApiModelProperty(value = "名称拼音首字母")
@TableField("pinyin")
private String pinyin;
@ApiModelProperty(value = "条形码")
@TableField("bar_code")
private String barCode;
@ApiModelProperty(value = "规格")
@TableField("spec")
private String spec;
@ApiModelProperty(value = "搜索关键字")
@TableField("keywords")
private String keywords;
@ApiModelProperty(value = "销售单位")
@TableField("unit")
private String unit;
@ApiModelProperty(value = "重量(预留,算运费用)")
@TableField("weight")
private Double weight;
@ApiModelProperty(value = "重量单位")
@TableField("weight_unit")
private String weightUnit;
@ApiModelProperty(value = "体积(预留,算运费用)")
@TableField("volumn")
private String volumn;
@ApiModelProperty(value = "是否纳入预算管控")
@TableField("check_budget")
private Boolean checkBudget;
@ApiModelProperty(value = "创建人")
@TableField("created_by")
private String createdBy;
@ApiModelProperty(value = "创建时间")
@TableField("created_date")
private Date createdDate;
@ApiModelProperty(value = "修改人")
@TableField("last_modified_by")
private String lastModifiedBy;
@ApiModelProperty(value = "修改时间")
@TableField("last_modified_date")
private Date lastModifiedDate;
@ApiModelProperty(value = "是否删除")
@TableField("is_deleted")
private Boolean isDeleted;
@TableField("catalog_id")
private Long catalogId;
@TableField("company_id")
private Long companyId;
@TableField("category_id")
private Long categoryId;
@TableField("brand_id")
private Long brandId;
@TableField("from_category_id")
private Long fromCategoryId;
@TableField("supply_company_id")
private Long supplyCompanyId;
@TableField("purchase_class_id")
private Long purchaseClassId;
@ApiModelProperty(value = "预算分类ID")
@TableField("budget_class_id")
private Integer budgetClassId;
@ApiModelProperty(value = "平台商品ID")
@TableField("base_product_id")
private Long baseProductId;
@ApiModelProperty(value = "平台商品code")
@TableField("base_product_code")
private String baseProductCode;
@ApiModelProperty(value = "预留字段A")
@TableField("field_a")
private String fieldA;
@ApiModelProperty(value = "预留字段B")
@TableField("field_b")
private String fieldB;
@ApiModelProperty(value = "预留字段C")
@TableField("field_c")
private String fieldC;
@ApiModelProperty(value = "预留字段D")
@TableField("field_d")
private String fieldD;
@ApiModelProperty(value = "预留字段E")
@TableField("field_e")
private String fieldE;
@ApiModelProperty(value = "ean码")
@TableField("ean_code")
private String eanCode;
@ApiModelProperty(value = "hsCode")
@TableField("hs_code")
private String hsCode;
@ApiModelProperty(value = "upc码")
@TableField("upc")
private String upc;
@TableField("platform_category_code")
private String platformCategoryCode;
@TableField("platform_category_id")
private Long platformCategoryId;
@ApiModelProperty(value = "运费模板id")
@TableField("freight_template_id")
private Long freightTemplateId;
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/search/service/ProductSpuService.java
================================================
package com.mcoding.modular.search.service;
import com.mcoding.modular.search.entity.ProductSpu;
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
* 商品SPU 服务类
*
*
* @author wzt
* @since 2022-06-24
*/
public interface ProductSpuService extends IService {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/search/service/impl/ProductSpuServiceImpl.java
================================================
package com.mcoding.modular.search.service.impl;
import com.mcoding.modular.search.entity.ProductSpu;
import com.mcoding.modular.search.dao.ProductSpuMapper;
import com.mcoding.modular.search.service.ProductSpuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
*
* 商品SPU 服务实现类
*
*
* @author wzt
* @since 2022-06-24
*/
@Service
public class ProductSpuServiceImpl extends ServiceImpl implements ProductSpuService {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/system/user/controller/SysUserController.java
================================================
package com.mcoding.modular.system.user.controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mcoding.base.core.orm.DslParser;
import com.mcoding.base.core.rest.*;
import com.mcoding.modular.system.user.service.SysUserService;
import com.mcoding.modular.system.user.entity.SysUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
*
* 管理员表
*
*
* @author wzt
* @since 2020-07-27
*/
@Api(tags = "系统-管理员服务")
@RestController
public class SysUserController {
@Resource
private SysUserService sysUserService;
@ApiOperation("创建")
@PostMapping("/service/sysuser/create")
public ResponseResult create(@Valid @RequestBody SysUser sysUser) {
sysUserService.save(sysUser);
return ResponseResult.success();
}
@ApiOperation(value = "删除")
@PostMapping("/service/sysuser/delete")
public ResponseResult delete(@RequestParam Integer id) {
sysUserService.removeById(id);
return ResponseResult.success();
}
@ApiOperation(value = "修改")
@PostMapping("/service/sysuser/modify")
public ResponseResult modify(@Valid @RequestBody SysUser sysUser) {
sysUserService.updateById(sysUser);
return ResponseResult.success();
}
@ApiOperation(value = "查询活动详情")
@GetMapping("/service/sysuser/detail")
public ResponseResult detail(@RequestParam Integer id) {
return ResponseResult.success(sysUserService.getById(id));
}
@ApiOperation(value = "分页查询")
@PostMapping("/service/sysuser/queryByPage")
public ResponseResult> queryByPage(@RequestBody JSONObject queryObject) {
DslParser dslParser = new DslParser<>(queryObject);
QueryWrapper queryWrapper = dslParser.parseToWrapper(SysUser.class);
IPage page = dslParser.generatePage();
sysUserService.page(page, queryWrapper);
return ResponseResult.success(page);
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/system/user/dao/SysUserMapper.java
================================================
package com.mcoding.modular.system.user.dao;
import com.mcoding.modular.system.user.entity.SysUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
*
* 管理员表 Mapper 接口
*
*
* @author wzt
* @since 2020-07-27
*/
public interface SysUserMapper extends BaseMapper {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/system/user/entity/SysUser.java
================================================
package com.mcoding.modular.system.user.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.TableName;
/**
*
* 管理员表
*
*
* @author wzt
* @since 2020-07-27
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_user")
@ApiModel(value="SysUser", description="管理员表")
public class SysUser implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "头像")
@TableField("avatar")
private String avatar;
@ApiModelProperty(value = "账号")
@TableField("account")
private String account;
@ApiModelProperty(value = "密码")
@TableField("password")
private String password;
@ApiModelProperty(value = "md5密码盐")
@TableField("salt")
private String salt;
@ApiModelProperty(value = "名字")
@TableField("name")
private String name;
@ApiModelProperty(value = "生日")
@TableField("birthday")
private Date birthday;
@ApiModelProperty(value = "性别(字典)")
@TableField("sex")
private String sex;
@ApiModelProperty(value = "电子邮件")
@TableField("email")
private String email;
@ApiModelProperty(value = "电话")
@TableField("phone")
private String phone;
@ApiModelProperty(value = "角色id(多个逗号隔开)")
@TableField("role_id")
private String roleId;
@ApiModelProperty(value = "部门id(多个逗号隔开)")
@TableField("dept_id")
private Long deptId;
@ApiModelProperty(value = "状态(字典)")
@TableField("status")
private String status;
@ApiModelProperty(value = "创建时间")
@TableField("create_time")
private Date createTime;
@ApiModelProperty(value = "创建人")
@TableField("create_user")
private Long createUser;
@ApiModelProperty(value = "更新时间")
@TableField("update_time")
private Date updateTime;
@ApiModelProperty(value = "更新人")
@TableField("update_user")
private Long updateUser;
@ApiModelProperty(value = "乐观锁")
@TableField("version")
private Integer version;
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/system/user/service/SysUserService.java
================================================
package com.mcoding.modular.system.user.service;
import com.mcoding.modular.system.user.entity.SysUser;
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
* 管理员表 服务类
*
*
* @author wzt
* @since 2020-07-27
*/
public interface SysUserService extends IService {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/system/user/service/impl/SysUserServiceImpl.java
================================================
package com.mcoding.modular.system.user.service.impl;
import com.mcoding.modular.system.user.entity.SysUser;
import com.mcoding.modular.system.user.dao.SysUserMapper;
import com.mcoding.modular.system.user.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
*
* 管理员表 服务实现类
*
*
* @author wzt
* @since 2020-07-27
*/
@Service
public class SysUserServiceImpl extends ServiceImpl implements SysUserService {
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/tech/job/ActivityStatusUpdateJob.java
================================================
package com.mcoding.modular.tech.job;
import com.mcoding.base.core.log.MdcLog;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author wzt on 2020/2/9.
* @version 1.0
*/
@Slf4j
@Component
public class ActivityStatusUpdateJob {
@MdcLog
@XxlJob(value = "activityStatusUpdateJob")
public ReturnT execute(String s) {
try {
// do the job
return ReturnT.SUCCESS;
} catch (Exception e) {
e.printStackTrace();
log.error("event=更新大套餐活动状态[异常]|{}", e.getMessage());
return ReturnT.FAIL;
}
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/tech/job/config/XxlJobConfig.java
================================================
package com.mcoding.modular.tech.job.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* @author wzt on 2019/11/20.
* @version 1.0
*/
@Slf4j
@Configuration
public class XxlJobConfig {
@Resource
private XxlJobPropertiesConfig xxlJobPropertiesConfig;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(xxlJobPropertiesConfig.getAdmin_addresses());
xxlJobSpringExecutor.setAppname(xxlJobPropertiesConfig.getAppname());
return xxlJobSpringExecutor;
}
}
================================================
FILE: backend/src/main/java/com/mcoding/modular/tech/job/config/XxlJobPropertiesConfig.java
================================================
package com.mcoding.modular.tech.job.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author wzt on 2019/11/18.
* @version 1.0
*/
@ConfigurationProperties(prefix = "xxl.job.executor")
@Component
@Data
public class XxlJobPropertiesConfig {
private String admin_addresses;
private String appname;
private String ip;
private Integer port;
private String accessToken;
private String logpath;
private Integer logretentiondays;
}
================================================
FILE: backend/src/main/resources/application-dev.properties
================================================
spring.datasource.druid.url=jdbc:mysql://47.95.192.230:3306/mcoding?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.connection-init-sqls=set names utf8mb4
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=false
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.filters=stat,wall,slf4j
spring.datasource.druid.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.allow=
spring.datasource.druid.stat-view-servlet.deny=
spring.datasource.druid.stat-view-servlet.reset-enable=false
spring.datasource.druid.stat-view-servlet.login-username=druid
spring.datasource.druid.stat-view-servlet.login-password=druid#123456
spring.redis.redisson.config=classpath:prop/redisson-dev.yaml
spring.session.store-type=redis
spring.session.timeout.seconds=900
elasticsearch.clusterNodes=127.0.0.1:9200
================================================
FILE: backend/src/main/resources/application.properties
================================================
spring.application.name=backend-api
server.port=8087
server.servlet.context-path=/backend-api
spring.profiles.active=dev
spring.mvc.servlet.load-on-startup=1
spring.main.allow-bean-definition-overriding=true
spring.jackson.serialization.write-dates-as-timestamps: true
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis-plus.configuration.log-impl=com.mcoding.base.core.log.MybatisLogImpl
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
================================================
FILE: backend/src/main/resources/config/flow.el.xml
================================================
THEN(a, b, c);
================================================
FILE: backend/src/main/resources/logback-spring.xml
================================================
logback
debug
${CONSOLE_LOG_PATTERN}
UTF-8
${log.path}/log_debug.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
100MB
15
debug
ACCEPT
DENY
${log.path}/log_info.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
100MB
15
info
ACCEPT
DENY
${log.path}/log_warn.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
100MB
15
warn
ACCEPT
DENY
${log.path}/log_error.log
${FILE_LOG_PATTERN}
UTF-8
${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
100MB
15
ERROR
ACCEPT
DENY
================================================
FILE: backend/src/main/resources/prop/redisson-dev.yaml
================================================
singleServerConfig:
address: "redis://47.95.192.230:6379"
password: redis#123
clientName: fish_api_dev
database: 0
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 3000
timeout: 5000
retryAttempts: 3
retryInterval: 1500
subscriptionsPerConnection: 5
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 8
connectionPoolSize: 16
dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec:
class: "org.redisson.codec.JsonJacksonCodec"
transportMode: "NIO"
================================================
FILE: backend/src/main/resources/template/UserTemplate.ftl
================================================
Document
| 序号 |
手机号码 |
昵称 |
用户名称 |
<#list userList as ad>
| ${ad_index+1} |
${ad.mobileNumber} |
${ad.nickName} |
${ad.userName ! ''} |
#list>
================================================
FILE: base-common/pom.xml
================================================
tropical_fish
com.mcoding
0.0.1-SNAPSHOT
4.0.0
base-common
${parent.version}
jar
base-common
1.8
4.1.4
org.jooq
joor-java-8
0.9.10
ma.glasnost.orika
orika-core
1.5.2
com.alibaba
fastjson
1.2.83
org.projectlombok
lombok
1.18.4
net.sourceforge.jexcelapi
jxl
2.6.12
com.alibaba
easyexcel
3.0.5
org.apache.commons
commons-lang3
3.4
org.apache.commons
commons-collections4
4.3
commons-io
commons-io
2.7
com.google.guava
guava
29.0-jre
org.codehaus.xfire
xfire-core
1.2.6
org.bouncycastle
bcprov-jdk16
1.46
cn.hutool
hutool-all
5.1.2
com.itextpdf
itext7-core
7.2.1
pom
com.itextpdf
html2pdf
4.0.1
org.freemarker
freemarker
2.3.30
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/exception/BizException.java
================================================
package com.mcoding.base.common.exception;
/**
* 业务异常
*
* @author wzt on 2020/3/9.
* @version 1.0
*/
public class BizException extends RuntimeException {
public BizException() {
super();
}
public BizException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public BizException(String message, Throwable cause) {
super(message, cause);
}
public BizException(String message) {
super(message);
}
public BizException(String message, Object data) {
super(message);
}
public BizException(Throwable cause) {
super(cause);
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/exception/CommonException.java
================================================
package com.mcoding.base.common.exception;
public class CommonException extends RuntimeException {
public CommonException() {
super();
}
public CommonException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public CommonException(String message, Throwable cause) {
super(message, cause);
}
public CommonException(String message) {
super(message);
}
public CommonException(String message, Object data) {
super(message);
}
public CommonException(Throwable cause) {
super(cause);
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/exception/SysException.java
================================================
package com.mcoding.base.common.exception;
/**
* 系统异常
*
* @author wzt on 2020/3/9.
* @version 1.0
*/
public class SysException extends RuntimeException {
public SysException() {
super();
}
public SysException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public SysException(String message, Throwable cause) {
super(message, cause);
}
public SysException(String message) {
super(message);
}
public SysException(String message, Object data) {
super(message);
}
public SysException(Throwable cause) {
super(cause);
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/command/CommandInvoker.java
================================================
package com.mcoding.base.common.pattern.command;
/**
* @author wzt on 2019/11/20.
* @version 1.0
*/
public class CommandInvoker implements ICommandInvoker {
@Override
public Result invoke(ICommand command) {
return command.execute(this);
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/command/ICommand.java
================================================
package com.mcoding.base.common.pattern.command;
public interface ICommand {
Result execute(ICommandInvoker context);
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/command/ICommandInvoker.java
================================================
package com.mcoding.base.common.pattern.command;
public interface ICommandInvoker {
public Result invoke(ICommand command);
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/filterchain/Filter.java
================================================
package com.mcoding.base.common.pattern.filterchain;
/**
* @author wzt on 2020/5/3.
* @version 1.0
*/
public interface Filter {
/**
* 过滤
*
* @param request
* @param filterContext
*/
void doFilter(Request request, FilterContext filterContext);
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/filterchain/FilterContext.java
================================================
package com.mcoding.base.common.pattern.filterchain;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* 责任链
*
* @author wzt on 2020/5/3.
* @version 1.0
*/
public class FilterContext {
private List> filterList = Lists.newArrayList();
private int offSet = 0;
@Getter
@Setter
private Target target;
@Getter
private Response executeResult;
public void doFilter(Request request) {
if (offSet < filterList.size()) {
Filter filter = filterList.get(offSet++);
filter.doFilter(request, this);
return;
}
executeResult = target.execute(request);
}
public void addFilter(Filter filter) {
filterList.add(filter);
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/filterchain/Target.java
================================================
package com.mcoding.base.common.pattern.filterchain;
/**
* @author wzt on 2020/5/3.
* @version 1.0
*/
@FunctionalInterface
public interface Target {
/**
* 执行目标方法
*
* @param request
* @return
*/
Response execute(Request request);
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/pipeline/Pipeline.java
================================================
package com.mcoding.base.common.pattern.pipeline;
/**
* @author wzt on 2020/5/4.
* @version 1.0
*/
public interface Pipeline {
Value getHead();
Value getTail();
void setTail(Value v);
void addValue(Value v);
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/pipeline/StandardPipeline.java
================================================
package com.mcoding.base.common.pattern.pipeline;
/**
* @author wzt on 2020/5/4.
* @version 1.0
*/
public class StandardPipeline {
protected Value head;
protected Value tail;
public Value getHead() {
return this.head;
}
public Value getTail() {
return this.tail;
}
public void setTail(Value v) {
this.tail = v;
}
public void addValue(Value v) {
if (head == null) {
head = v;
v.setNext(tail);
return;
}
Value current = head;
while (current != null) {
if (current.getNext() == tail) {
current.setNext(v);
v.setNext(tail);
break;
}
current = current.getNext();
}
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/pattern/pipeline/Value.java
================================================
package com.mcoding.base.common.pattern.pipeline;
/**
* @author wzt on 2020/5/4.
* @version 1.0
*/
public abstract class Value {
public Value next;
public Value getNext() {
return next;
}
public void setNext(Value v) {
this.next = v;
}
public abstract void invoke(T s);
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/Assert.java
================================================
package com.mcoding.base.common.util;
import com.mcoding.base.common.exception.CommonException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.List;
/**
* 帮助验证参数的断言工具
*
* @author hzy
*/
public class Assert {
/**
* 列表不能为空,否则就报错
*
* @param list
* @param defaultMsg
*/
@SuppressWarnings("rawtypes")
public static void isNotEmpty(Collection list, String defaultMsg) {
if (CollectionUtils.isEmpty(list))
throw new CommonException(defaultMsg);
}
/**
* 列表不能为空,否则就报错
*
* @param list
* @param defaultMsg
* @param i18n
*/
@SuppressWarnings("rawtypes")
public static void isNotEmpty(List list, String defaultMsg, String i18n) {
if (CollectionUtils.isEmpty(list))
throw new CommonException(defaultMsg, i18n);
}
/**
* 值不能为空,如果是空则报错
*
* @param value
* @param defaultMsg
*/
public static void isNotBlank(String value, String defaultMsg) {
if (StringUtils.isBlank(value))
throw new CommonException(defaultMsg);
}
/**
* 值不能为空,如果是空则报错
*
* @param value
* @param defaultMsg
* @param i18n
*/
public static void isNotBlank(String value, String defaultMsg, String i18n) {
if (StringUtils.isBlank(value))
throw new CommonException(defaultMsg, i18n);
}
/**
* 参数不能为空,为空报错
*
* @param type
* @param mss
*/
public static void isNotNull(Object type, String mss) {
if (type == null)
throw new CommonException(mss);
}
/**
* 参数不能为空,为空报错
*
* @param type
*/
public static void isNotNull(Object type) {
if (type == null)
throw new CommonException(type + "不能为空");
}
/**
* 值应该存在。如果不存在,则报错
*
* @param list
* @param value
*/
public static void isExists(List list, T value, String msg) {
if (!list.contains(value))
throw new CommonException(StringUtils.defaultIfBlank(msg, "该值不存在"));
}
/**
* 值应该不存在存在。如果存在,则报错
*
* @param obj
* @param str
*/
public static void doNotExists(List list, T value, String msg) {
if (list.contains(value))
throw new CommonException(StringUtils.defaultIfBlank(msg, "该值已经存在"));
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/bean/BeanMapperUtils.java
================================================
package com.mcoding.base.common.util.bean;
import cn.hutool.core.collection.CollectionUtil;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.Type;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* 实体熟悉映射工具
*/
public abstract class BeanMapperUtils {
private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
public static D map(S source, Class clazz) {
if (Objects.isNull(source)) {
return null;
}
MapperFacade mapper = mapperFactory.getMapperFacade();
return mapper.map(source, clazz);
}
public static void map(S source, D destination) {
if (Objects.isNull(source) || Objects.isNull(destination)) {
return;
}
MapperFacade mapper = mapperFactory.getMapperFacade();
mapper.map(source, destination);
}
public static D map(S s, Type sType, Type dType) {
if (s == null) {
return null;
}
MapperFacade mapper = mapperFactory.getMapperFacade();
return mapper.map(s, sType, dType);
}
public static List mapAsList(List source, Class clazz) {
if (CollectionUtil.isEmpty(source)) {
return Collections.emptyList();
}
MapperFacade mapper = mapperFactory.getMapperFacade();
return mapper.mapAsList(source, clazz);
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/collection/MapUtils.java
================================================
package com.mcoding.base.common.util.collection;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* @author wzt on 2020/1/16.
* @version 1.0
*/
public class MapUtils {
/**
* 对每个分组求和
*
* @param sourceMap
* @param function
* @param
* @param
* @return
*/
public static Map sumEachGroupList(Map> sourceMap, Function function) {
Map resultMap = new HashMap<>(sourceMap.size());
sourceMap.forEach((key, list) -> {
BigDecimal sum = BigDecimal.ZERO;
for (S t : list) {
R result = function.apply(t);
sum = sum.add(new BigDecimal(result.toString()));
}
resultMap.put(key, sum);
});
return resultMap;
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/constant/MdcConstants.java
================================================
package com.mcoding.base.common.util.constant;
/**
* @author wzt on 2020/4/4.
* @version 1.0
*/
public class MdcConstants {
public static final String TRACE_ID = "traceID";
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/constant/SysConstants.java
================================================
package com.mcoding.base.common.util.constant;
/**
* @author wzt on 2020/3/30.
* @version 1.0
*/
public interface SysConstants {
int YES = 1;
int NO = 0;
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/date/DateTimeUtils.java
================================================
package com.mcoding.base.common.util.date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Optional;
/**
* @author mazehong
* @date 2020/3/26
*/
public class DateTimeUtils {
/**
* Date转LocalDateTime
*
* @param date
* @return
*/
public static LocalDateTime toLocalDateTime(Date date) {
Optional.ofNullable(date).orElseThrow(() -> new IllegalArgumentException("Date转LocalDateTime异常"));
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).withNano(0);
}
/**
* LocalDateTime转Date
*
* @param localDateTime
* @return
*/
public static Date toDate(LocalDateTime localDateTime) {
Optional.ofNullable(localDateTime).orElseThrow(() -> new IllegalArgumentException("LocalDateTime转Date异常"));
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
}
================================================
FILE: base-common/src/main/java/com/mcoding/base/common/util/date/DateUtils.java
================================================
package com.mcoding.base.common.util.date;
import cn.hutool.core.lang.Assert;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
/**
* @author mazehong
* @date 2020/3/3
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtils {
public static final String SHORT_DATE_FORMAT = "yyyy-MM-dd";
public static final String SHORT_DATE_GBK_FORMAT = "yyyy年MM月dd日";
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DATE_GBK_FORMAT = "yyyy年MM月dd日 HH时mm分";
public static final String LONG_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String LONG_DATE_GBK_FORMAT = "yyyy年MM月dd日 HH时mm分ss秒";
public static final String MAIL_DATE_FORMAT = "yyyyMMddHHmmss";
public static final String MAIL_DATE_HHMM_FORMAT = "HH:mm";
public static final String FULL_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss:SSS";
public static final String SQL_FULL_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
public static final String FULL_DATE_GBK_FORMAT = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒";
public static final String FULL_DATE_COMPACT_FORMAT = "yyyyMMddHHmmssSSS";
public static final String LDAP_DATE_FORMAT = "yyyyMMddHHmm'Z'";
public static final String US_LOCALE_DATE_FORMAT = "EEE MMM dd HH:mm:ss zzz yyyy";
public static final String MAIL_DATE_DT_PART_FORMAT = "yyyyMMdd";
public static final String MAIL_TIME_TM_PART_FORMAT = "HHmmss";
public static final String LONG_DATE_TM_PART_FORMAT = "HH:mm:ss";
public static final String LONG_DATE_TM_PART_GBK_FORMAT = "HH时mm分ss秒";
public static final String MAIL_DATA_DTM_PART_FORMAT = "MM月dd日HH:mm";
public static final String POINT_DATA_DTM_PART_FORMAT = "yyyy.MM.dd";
public static final String DEFAULT_DATE_FORMAT = US_LOCALE_DATE_FORMAT;
public static final long NANO_ONE_SECOND = 1000;
public static final long NANO_ONE_MINUTE = 60 * NANO_ONE_SECOND;
public static final long NANO_ONE_HOUR = 60 * NANO_ONE_MINUTE;
public static final long NANO_ONE_DAY = 24 * NANO_ONE_HOUR;
/**
* 5分钟
*/
public static final long FIVE_MINUTE = 5 * NANO_ONE_MINUTE;
/**
* 3天
*/
public static final long THREE_DAY = 3 * NANO_ONE_DAY;
public static final String MORE_THAN = ">";
public static final String LESS_THAN = "<";
public static final String EQUAL = "=";
public static final String DASH = "-";
public static final String COLON = ":";
public static final String BLANK = " ";
/**
* 字符串转日期,模糊判断, 超过10的则精确到秒,反之精确到天
*
* @param arg
* @return
* @throws java.text.ParseException
*/
public static Date ignoreDate(String arg) throws ParseException {
SimpleDateFormat ACCURATE_SECONDS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat ACCURATE_DAYS = new SimpleDateFormat("yyyy-MM-dd");
return arg.length() > 10 ? ACCURATE_SECONDS.parse(arg) : ACCURATE_DAYS.parse(arg);
}
/**
* 获取当前日期类型时间
*/
public static Date getNow() {
return new Date();
}
/**
* 获取当前时间戳
*/
public static long getNowTimestamp() {
return getNow().getTime();
}
/**
* 获取当前日期 yyyyMMdd
*/
public static String getCurrentDate() {
return toMailDateDtPartString(getNow());
}
/**
* 获取当期时间HHmmss
*
* @return
*/
public static String getCurrentTime() {
return toMailTimeTmPartString(getNow());
}
/**
* 获取当期时间MM月dd日HH:mm
*
* @return
*/
public static String getCurrentMmDdHmTime() {
return toMailDtmPart(getNow());
}
/**
* 获取当前日期和时间yyyyMMddHHmmss
*
* @return
*/
public static String getCurrentDateTime() {
return toMailDateString(getNow());
}
//============================1.Date2String=====================================
/**
* 将一个日期型转换为指定格式字串
*
* @param aDate
* @param formatStr
* @return
*/
public static final String toFormatDateString(Date aDate, String formatStr) {
if (aDate == null) {
return StringUtils.EMPTY;
}
return new SimpleDateFormat(formatStr).format(aDate);
}
/**
* 将一个日期型转换为'yyyy-MM-dd'格式字串
*
* @param aDate
* @return
*/
public static final String toShortDateString(Date aDate) {
return toFormatDateString(aDate, SHORT_DATE_FORMAT);
}
/**
* 将一个日期型转换为'yyyyMMdd'格式字串
*
* @param aDate
* @return
*/
public static final String toMailDateDtPartString(Date aDate) {
return toFormatDateString(aDate, MAIL_DATE_DT_PART_FORMAT);
}
/**
* 将一个日期型转换为'HHmmss'格式字串
*
* @param aDate
* @return
*/
public static final String toMailTimeTmPartString(Date aDate) {
return toFormatDateString(aDate, MAIL_TIME_TM_PART_FORMAT);
}
/**
* 将一个日期型转换为'yyyyMMddHHmmss'格式字串
*
* @param aDate
* @return
*/
public static final String toMailDateString(Date aDate) {
return toFormatDateString(aDate, MAIL_DATE_FORMAT);
}
/**
*
*/
/**
* 将一个日期型转换为MM月dd日HH:mm格式字串
*
* @param aDate
* @return
*/
public static final String toMailDtmPart(Date aDate) {
return toFormatDateString(aDate, MAIL_DATA_DTM_PART_FORMAT);
}
/**
*
*/
/**
* 将一个日期型转换为yyyy.MM.dd格式字串
*
* @param aDate
* @return
*/
public static final String toPointDtmPart(Date aDate) {
return toFormatDateString(aDate, POINT_DATA_DTM_PART_FORMAT);
}
/**
* 将一个日期型转换为'yyyy-MM-dd HH:mm:ss'格式字串
*
* @param aDate
* @return
*/
public static final String toLongDateString(Date aDate) {
return toFormatDateString(aDate, LONG_DATE_FORMAT);
}
/**
* 将一个日期型转换为'HH:mm:ss'格式字串
*
* @param aDate
* @return
*/
public static final String toLongDateTmPartString(Date aDate) {
return toFormatDateString(aDate, LONG_DATE_TM_PART_FORMAT);
}
/**
* 将一个日期型转换为'yyyy年MM月dd日'格式字串
*
* @param aDate
* @return
*/
public static final String toShortDateGBKString(Date aDate) {
return toFormatDateString(aDate, SHORT_DATE_GBK_FORMAT);
}
/**
* 将一个日期型转换为'yyyy年MM月dd日 HH时mm分'格式字串
*
* @param aDate
* @return
*/
public static final String toDateGBKString(Date aDate) {
return toFormatDateString(aDate, DATE_GBK_FORMAT);
}
/**
* 将一个日期型转换为'yyyy年MM月dd日 HH时mm分ss秒'格式字串
*
* @param aDate
* @return
*/
public static final String toLongDateGBKString(Date aDate) {
return toFormatDateString(aDate, LONG_DATE_GBK_FORMAT);
}
/**
* 将一个日期型转换为'HH时mm分ss秒'格式字串
*
* @param aDate
* @return
*/
public static final String toLongDateTmPartGBKString(Date aDate) {
return toFormatDateString(aDate, LONG_DATE_TM_PART_GBK_FORMAT);
}
/**
* 将一个日期型转换为'yyyy-MM-dd HH:mm:ss:SSS'格式字串
*
* @param aDate
* @return
*/
public static final String toFullDateString(Date aDate) {
return toFormatDateString(aDate, FULL_DATE_FORMAT);
}
/**
* 将一个日期型转换为'yyyy年MM月dd日 HH时mm分ss秒SSS毫秒'格式字串
*
* @param aDate
* @return
*/
public static final String toFullDateGBKString(Date aDate) {
return toFormatDateString(aDate, FULL_DATE_GBK_FORMAT);
}
/**
* 将一个日期型转换为'yyyyMMddHHmmssSSS'格式字串
*
* @param aDate
* @return
*/
public static final String toFullDateCompactString(Date aDate) {
return toFormatDateString(aDate, FULL_DATE_COMPACT_FORMAT);
}
/**
* 将一个日期型转换为LDAP格式字串
*
* @param aDate
* @return
*/
public static final String toLDAPDateString(Date aDate) {
return toFormatDateString(aDate, LDAP_DATE_FORMAT);
}
//============================2.String2Date=====================================
/**
* 将一个符合指定格式的字串解析成日期型
*
* @param aDateStr
* @param formatter
* @return
* @throws java.text.ParseException
*/
public static final Date parser(String aDateStr, String formatter) throws ParseException {
if (StringUtils.isBlank(aDateStr)) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(formatter);
return sdf.parse(aDateStr);
}
/**
* 将一个符合指定格式的字串解析成日期型
*
* @param aDateStr
* @param formatter
* @param lenient false表示需要对字符串进行严格校验,有多余的空格都不行 true表示不进行严格校验,是SimpleDateFormat默认的方式
* @return
* @throws java.text.ParseException
*/
public static final Date parser(String aDateStr, String formatter, boolean lenient) throws ParseException {
if (StringUtils.isBlank(aDateStr)) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(formatter);
sdf.setLenient(lenient);
return sdf.parse(aDateStr);
}
/**
* 将一个符合'yyyy-MM-dd HH:mm:ss'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseLongDateString(String aDateStr) throws ParseException {
return parser(aDateStr, LONG_DATE_FORMAT);
}
/**
* 将一个符合'yyyy-MM-dd HH:mm:ss'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseLongDateDtPartString(String aDateStr) throws ParseException {
return parser(aDateStr, LONG_DATE_FORMAT);
}
/**
* 将一个符合'yyyy-MM-dd HH:mm:ss'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseLongDateTmPartString(String aDateStr) throws ParseException {
return parser(aDateStr, LONG_DATE_FORMAT);
}
/**
* 将一个符合'yyyy-MM-dd'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseShortDateString(String aDateStr) throws ParseException {
return parser(aDateStr, SHORT_DATE_FORMAT);
}
/**
* 将一个符合'yyyyMMddHHmmss'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseMailDateString(String aDateStr) throws ParseException {
return parser(aDateStr, MAIL_DATE_FORMAT);
}
/**
* 将一个符合'yyyyMMdd'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseMailDateDtPartString(String aDateStr) throws ParseException {
return parser(aDateStr, MAIL_DATE_DT_PART_FORMAT);
}
/**
* 将一个符合'HHmmss'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseMailDateTmPartString(String aDateStr) throws ParseException {
return parser(aDateStr, MAIL_TIME_TM_PART_FORMAT);
}
/**
* 将一个符合'yyyy-MM-dd HH:mm:ss:SSS'格式的字串解析成日期型
*
* @param aDateStr
* @return
*/
public static final Date parseFullDateString(String aDateStr) throws ParseException {
return parser(aDateStr, FULL_DATE_FORMAT);
}
/**
* 将一个符合'yyyy-MM-dd'、'yyyy-MM-dd HH:mm:ss'或'EEE MMM dd HH:mm:ss zzz
* yyyy'格式的字串解析成日期型, 如果为blank则返回空,如果不为blank又不符合格式则报错
*
* @param aDateStr
* @return
*/
public static Date parseDateString(String aDateStr) {
Date ret = null;
if (StringUtils.isNotBlank(aDateStr)) {
try {
if (DateValidator.isLongDateStr(aDateStr)) {
ret = parseLongDateString(aDateStr);
} else if (DateValidator.isShortDateStr(aDateStr)) {
ret = parseShortDateString(aDateStr);
} else if (DateValidator.isDateStrMatched(aDateStr, DEFAULT_DATE_FORMAT)) {
ret = parser(aDateStr, DEFAULT_DATE_FORMAT);
} else {
throw new IllegalArgumentException("date format mismatch");
}
} catch (ParseException e) {
log.warn("parseDateString failed", e);
}
}
return ret;
}
//============================3.String2String=====================================
/**
* 转换日期格式 yyyy-MM-dd => yyyyMMdd
*
* @param dt yyyy-MM-dd
* @return yyyyMMdd
*/
public static String transfer2ShortDate(String dt) {
if (dt == null || dt.length() != 10) {
return dt;
}
Assert.notNull(dt, "格式错误");
String[] tmp = StringUtils.split(dt, DASH);
Assert.isTrue(tmp != null && tmp.length == 3, "格式错误");
return tmp[0].concat(StringUtils.leftPad(tmp[1], 2, "0")).concat(StringUtils.leftPad(tmp[2], 2, "0"));
}
/**
* 转换日期格式 yyyyMMdd HH:mm:ss => yyyy-MM-dd HH:mm:ss
*
* @param dt yyyyMMdd
* @param tm HHmmss
* @return yyyy-MM-dd HH:mm:ss
*/
public static String transfer2LongDatePart(String dt, String tm) {
return transfer2LongDateDtPart(dt).concat(BLANK).concat(transfer2LongDateTmPart(tm));
}
/**
* 转换日期格式 yyyyMMdd => yyyy-MM-dd
*
* @param dt yyyyMMdd
* @return yyyy-MM-dd
*/
public static String transfer2LongDateDtPart(String dt) {
if (dt == null || dt.length() != 8) {
return dt;
}
Assert.notNull(dt, "格式错误");
Assert.isTrue(dt.length() == 8, "格式错误");
return dt.substring(0, 4).concat(DASH).concat(dt.substring(4, 6)).concat(DASH).concat(dt.substring(6));
}
/**
* 转换日期格式 HHmmss => HH:mm:ss
*
* @param tm HHmmss
* @return HH:mm:ss
*/
public static String transfer2LongDateTmPart(String tm) {
if (tm == null || tm.length() != 6) {
return tm;
}
Assert.notNull(tm, "格式错误");
Assert.isTrue(tm.length() == 6, "格式错误");
return tm.substring(0, 2).concat(COLON).concat(tm.substring(2, 4)).concat(COLON).concat(tm.substring(4));
}
/**
* 转换日期格式 yyyyMMdd => yyyy年MM月dd日
*
* @param dt yyyyMMdd
* @return yyyy年MM月dd日
*/
public static String transfer2LongDateGbkDtPart(String dt) {
if (dt == null || dt.length() != 8) {
return dt;
}
Assert.notNull(dt, "格式错误");
Assert.isTrue(dt.length() == 8, "格式错误");
return dt.substring(0, 4).concat("年").concat(dt.substring(4, 6)).concat("月").concat(dt.substring(6))
.concat("日");
}
/**
* 转换日期格式 yyyyMMdd => yyyy/MM/dd
*
* @param dt yyyyMMdd
* @return yyyy年MM月dd日
*/
public static String transfer2LongDate(String dt) {
if (dt == null || dt.length() != 8) {
return dt;
}
Assert.notNull(dt, "格式错误");
Assert.isTrue(dt.length() == 8, "格式错误");
return dt.substring(0, 4).concat("-").concat(dt.substring(4, 6)).concat("-").concat(dt.substring(6));
}
/**
* 转换日期格式HHmmss => HH时mm分ss秒
*
* @param tm HHmmss
* @return HH时mm分ss秒
*/
public static String transfer2LongDateGbkTmPart(String tm) {
if (tm == null || tm.length() != 6) {
return tm;
}
Assert.notNull(tm, "格式错误");
Assert.isTrue(tm.length() == 6, "格式错误");
return tm.substring(0, 2).concat("时").concat(tm.substring(2, 4)).concat("分").concat(tm.substring(4))
.concat("秒");
}
//============================4.时间加减=====================================
/**
* 为一个日期加上指定年数
*
* @param aDate
* @param amount 年数
* @return
*/
public static final Date addYears(Date aDate, int amount) {
return addTime(aDate, Calendar.YEAR, amount);
}
/**
* 为一个日期加上指定月数
*
* @param aDate
* @param amount 月数
* @return
*/
public static final Date addMonths(Date aDate, int amount) {
return addTime(aDate, Calendar.MONTH, amount);
}
/**
* 为一个日期加上指定天数
*
* @param aDate
* @param amount 天数
* @return
*/
public static final Date addDays(Date aDate, int amount) {
return addTime(aDate, Calendar.DAY_OF_YEAR, amount);
}
/**
* 为一个日期加上指定天数
*
* @param aDate yyyyMMdd格式字串
* @param amount 天数
* @return
*/
public static final String addDays(String aDate, int amount) {
try {
return toMailDateDtPartString(addTime(parseMailDateDtPartString(aDate), Calendar.DAY_OF_YEAR, amount));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* 为一个日期加上指定小时数
*
* @param aDate
* @param amount 小时数
* @return
*/
public static final Date addHours(Date aDate, int amount) {
return addTime(aDate, Calendar.HOUR, amount);
}
/**
* 为一个日期加上指定分钟数
*
* @param aDate
* @param amount 分钟数
* @return
*/
public static final Date addMinutes(Date aDate, int amount) {
return addTime(aDate, Calendar.MINUTE, amount);
}
/**
* 为一个日期加上指定秒数
*
* @param aDate
* @param amount 秒数
* @return
*/
public static final Date addSeconds(Date aDate, int amount) {
return addTime(aDate, Calendar.SECOND, amount);
}
private static final Date addTime(Date aDate, int timeType, int amount) {
if (aDate == null) {
return null;
}
Calendar cal = Calendar.getInstance();
cal.setTime(aDate);
cal.add(timeType, amount);
return cal.getTime();
}
//======================================5.时间国际化=================================
/**
* 得到当前时间的UTC时间
*
* @return
*/
public static final String getUTCTime() {
return getSpecifiedZoneTime(Calendar.getInstance().getTime(), TimeZone.getTimeZone("GMT+0"));
}
/**
* 得到指定时间的UTC时间
*
* @param aDate 时间戳
* @return yyyy-MM-dd HH:mm:ss 格式
*/
public static final String getUTCTime(Date aDate) {
return getSpecifiedZoneTime(aDate, TimeZone.getTimeZone("GMT+0"));
}
/**
* 得到当前时间的指定时区的时间
*
* @param tz
* @return
*/
public static final String getSpecifiedZoneTime(TimeZone tz) {
return getSpecifiedZoneTime(Calendar.getInstance().getTime(), tz);
}
/**
* 得到指定时间的指定时区的时间
*
* @param aDate 时间戳,Date是一个瞬间的long型距离历年的位移偏量,
* 在不同的指定的Locale/TimeZone的jvm中,它toString成不同的显示值,
* 所以没有必要为它再指定一个TimeZone变量表示获取它时的jvm的TimeZone
* @param tz 要转换成timezone
* @return yyyy-MM-dd HH:mm:ss 格式
*/
public static final String getSpecifiedZoneTime(Date aDate, TimeZone tz) {
if (aDate == null) {
return StringUtils.EMPTY;
}
Assert.notNull(tz, "格式错误");
SimpleDateFormat sdf = new SimpleDateFormat(LONG_DATE_FORMAT);
sdf.setTimeZone(tz);
return sdf.format(aDate);
}
//==================================6. miscellaneous==========================
/**
* 计算两个日期之间相差的月数
*
* @param startDate
* @param endDate
* @return
*/
public static final int getDifferenceMonths(Date startDate, Date endDate) {
Assert.notNull(startDate, "格式错误");
Assert.notNull(endDate, "格式错误");
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
return Math.abs((startCal.get(Calendar.YEAR) - endCal.get(Calendar.YEAR)) * 12
+ (startCal.get(Calendar.MONTH) - endCal.get(Calendar.MONTH)));
}
/**
* 计算两个日期之间相差的月数
*
* @param startDateStr yyyy-mm-dd
* @param endDateStr yyyy-mm-dd
* @return
*/
public static final int getDifferenceMonths(String startDateStr, String endDateStr) {
DateValidator.checkShortDateStr(startDateStr);
DateValidator.checkShortDateStr(endDateStr);
try {
return getDifferenceMonths(parseShortDateString(startDateStr), parseShortDateString(endDateStr));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* 获取两个日期间的月份,返回一个list,包含如下内容:yyyy-MM
* @return
*/
public static List