diff --git a/opsli-api/src/main/java/org/opsli/api/web/system/options/OptionsApi.java b/opsli-api/src/main/java/org/opsli/api/web/system/options/OptionsApi.java new file mode 100644 index 00000000..49e6eea2 --- /dev/null +++ b/opsli-api/src/main/java/org/opsli/api/web/system/options/OptionsApi.java @@ -0,0 +1,152 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opsli.api.web.system.options; + + +import org.opsli.api.base.result.ResultVo; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +import org.opsli.api.wrapper.system.options.OptionsModel; + + + +/** + * @BelongsProject: opsli-boot + + * @BelongsPackage: org.opsli.api.web.sys.options + + * @Author: Parker + * @CreateTime: 2021-02-07 18:24:38 + * @Description: 系统参数 + * + * 对外 API 直接 暴露 @GetMapping 或者 @PostMapping + * 对内也推荐 单机版 不需要设置 Mapping 但是调用方法得从Controller写起 + * + * 这样写法虽然比较绕,但是当单体项目想要改造微服务架构时 时非常容易的 + * + * + */ +public interface OptionsApi { + + /** 标题 */ + String TITLE = "系统参数"; + + /** + * 系统参数 查一条 + * @param model 模型 + * @return ResultVo + */ + @GetMapping("/get") + ResultVo get(OptionsModel model); + + /** + * 系统参数 查询分页 + * @param pageNo 当前页 + * @param pageSize 每页条数 + * @param request request + * @return ResultVo + */ + @GetMapping("/findPage") + ResultVo findPage( + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest request + ); + + /** + * 系统参数 新增 + * @param model 模型 + * @return ResultVo + */ + @PostMapping("/insert") + ResultVo insert(@RequestBody OptionsModel model); + + /** + * 系统参数 修改 + * @param model 模型 + * @return ResultVo + */ + @PostMapping("/update") + ResultVo update(@RequestBody OptionsModel model); + + /** + * 系统参数 删除 + * @param id ID + * @return ResultVo + */ + @PostMapping("/del") + ResultVo del(String id); + + /** + * 系统参数 批量删除 + * @param ids ID 数组 + * @return ResultVo + */ + @PostMapping("/delAll") + ResultVo delAll(String ids); + + /** + * 系统参数 Excel 导出 + * + * 导出时,Token认证和方法权限认证 全部都由自定义完成 + * 因为在 导出不成功时,需要推送错误信息, + * 前端直接走下载流,当失败时无法获得失败信息,即使前后端换一种方式后端推送二进制文件前端再次解析也是最少2倍的耗时 + * ,且如果数据量过大,前端进行渲染时直接会把浏览器卡死 + * 而直接开启socket接口推送显然是太过浪费资源了,所以目前采用Java最原始的手段 + * response 推送 javascript代码 alert 提示报错信息 + * + * @param request request + * @param response response + * @return ResultVo + */ + @GetMapping("/exportExcel") + void exportExcel(HttpServletRequest request, HttpServletResponse response); + + /** + * 系统参数 Excel 导入 + * @param request 文件流 request + * @return ResultVo + */ + @PostMapping("/importExcel") + ResultVo importExcel(MultipartHttpServletRequest request); + + /** + * 系统参数 Excel 下载导入模版 + * @param response response + * @return ResultVo + */ + @GetMapping("/importExcel/template") + void importTemplate(HttpServletResponse response); + + // ========================== + + /** + * 系统参数 查一条 + * @param optionCode 参数编号 + * @return ResultVo + */ + @GetMapping("/getByCode") + ResultVo getByCode(String optionCode); + +} diff --git a/opsli-api/src/main/java/org/opsli/api/wrapper/system/options/OptionsModel.java b/opsli-api/src/main/java/org/opsli/api/wrapper/system/options/OptionsModel.java new file mode 100644 index 00000000..422adf3f --- /dev/null +++ b/opsli-api/src/main/java/org/opsli/api/wrapper/system/options/OptionsModel.java @@ -0,0 +1,83 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opsli.api.wrapper.system.options; + + +import java.util.Date; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opsli.api.base.warpper.ApiWrapper; +import org.opsli.common.annotation.validation.ValidationArgs; +import org.opsli.common.annotation.validation.ValidationArgsLenMax; +import org.opsli.common.enums.ValiArgsType; +import org.opsli.plugins.excel.annotation.ExcelInfo; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * @BelongsProject: opsli-boot + + * @BelongsPackage: org.opsli.api.wrapper.sys.options + + * @Author: Parker + * @CreateTime: 2021-02-07 18:24:38 + * @Description: 系统参数 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class OptionsModel extends ApiWrapper { + + + + /** 参数编号 */ + @ApiModelProperty(value = "参数编号") + @ExcelProperty(value = "参数编号", order = 1) + @ExcelInfo + @ValidationArgs({ValiArgsType.IS_NOT_NULL, ValiArgsType.IS_GENERAL}) + @ValidationArgsLenMax(100) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String optionCode; + + + + /** 参数名称 */ + @ApiModelProperty(value = "参数名称") + @ExcelProperty(value = "参数名称", order = 2) + @ExcelInfo + @ValidationArgs({ValiArgsType.IS_NOT_NULL, ValiArgsType.IS_GENERAL_WITH_CHINESE}) + @ValidationArgsLenMax(200) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String optionName; + + + + /** 参数值 */ + @ApiModelProperty(value = "参数值") + @ExcelProperty(value = "参数值", order = 3) + @ExcelInfo + @ValidationArgsLenMax(500) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String optionValue; + + + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/OptionHandler.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/OptionHandler.java new file mode 100644 index 00000000..94c6f65c --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/OptionHandler.java @@ -0,0 +1,77 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opsli.core.cache.pushsub.handler; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.common.constants.CacheConstants; +import org.opsli.core.cache.local.CacheUtil; +import org.opsli.core.cache.pushsub.enums.MsgArgsType; +import org.opsli.core.cache.pushsub.enums.PushSubType; +import org.opsli.core.utils.OptionsUtil; +import org.opsli.plugins.cache.EhCachePlugin; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.pushsub.handler + * @Author: Parker + * @CreateTime: 2020-09-15 16:24 + * @Description: 系统参数消息处理 + */ +@Slf4j +public class OptionHandler implements RedisPushSubHandler{ + + @Autowired + private EhCachePlugin ehCachePlugin; + + @Override + public PushSubType getType() { + return PushSubType.OPTION; + } + + @Override + public void handler(JSONObject msgJson) { + // 系统参数刷新 + this.optionHandler(msgJson); + } + + /** + * 用户数据处理 + * @param msgJson + */ + private void optionHandler(JSONObject msgJson){ + JSONObject data = msgJson.getJSONObject(MsgArgsType.OPTION_MODEL_DATA.toString()); + // 数据为空则不执行 + if(data == null){ + return; + } + + // 获得参数编号 + String optionCode = (String) msgJson.get(MsgArgsType.OPTION_CODE.toString()); + if(StringUtils.isEmpty(optionCode)){ + return; + } + + String cacheKey = CacheUtil.handleKey(OptionsUtil.PREFIX_CODE + optionCode); + + // 先删除 + ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey); + } + + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/OptionMsgFactory.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/OptionMsgFactory.java new file mode 100644 index 00000000..cb86c6c6 --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/OptionMsgFactory.java @@ -0,0 +1,61 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opsli.core.cache.pushsub.msgs; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; +import lombok.experimental.Accessors; +import org.opsli.api.wrapper.system.menu.MenuModel; +import org.opsli.api.wrapper.system.options.OptionsModel; +import org.opsli.core.cache.pushsub.enums.MsgArgsType; +import org.opsli.core.cache.pushsub.enums.PushSubType; +import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver; +import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.pushsub.msgs + * @Author: Parker + * @CreateTime: 2020-09-15 16:50 + * @Description: 参数消息 + */ + +@Data +@Accessors(chain = true) +public final class OptionMsgFactory extends BaseSubMessage{ + + /** 通道 */ + private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL; + + private OptionMsgFactory(){} + + /** + * 构建消息 - 参数 + */ + public static BaseSubMessage createOptionMsg(OptionsModel optionsModel){ + BaseSubMessage baseSubMessage = new BaseSubMessage(); + // 数据 + JSONObject jsonObj = new JSONObject(); + jsonObj.put(MsgArgsType.OPTION_CODE.toString(), optionsModel.getOptionCode()); + jsonObj.put(MsgArgsType.OPTION_MODEL_DATA.toString(), optionsModel); + + // 参数 + baseSubMessage.build(CHANNEL,PushSubType.OPTION.toString(),jsonObj); + return baseSubMessage; + } + + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OptionsUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OptionsUtil.java new file mode 100644 index 00000000..8c1e7030 --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OptionsUtil.java @@ -0,0 +1,180 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opsli.core.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.api.base.result.ResultVo; +import org.opsli.api.web.system.menu.MenuApi; +import org.opsli.api.web.system.options.OptionsApi; +import org.opsli.api.wrapper.system.menu.MenuModel; +import org.opsli.api.wrapper.system.options.OptionsModel; +import org.opsli.core.cache.local.CacheUtil; +import org.opsli.core.cache.pushsub.msgs.MenuMsgFactory; +import org.opsli.core.cache.pushsub.msgs.OptionMsgFactory; +import org.opsli.core.msg.CoreMsg; +import org.opsli.plugins.redis.RedisPlugin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import static org.opsli.common.constants.OrderConstants.UTIL_ORDER; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.utils + * @Author: Parker + * @CreateTime: 2020-09-19 20:03 + * @Description: 参数工具类 + */ +@Slf4j +@Order(UTIL_ORDER) +@Component +@Lazy(false) +public class OptionsUtil { + + /** 前缀 */ + public static final String PREFIX_CODE = "options:code:"; + + + /** Redis插件 */ + private static RedisPlugin redisPlugin; + + /** 参数 Api */ + private static OptionsApi optionsApi; + + + /** + * 根据 optionCode 获得参数 + * @param optionCode + * @return + */ + public static OptionsModel getOptionByCode(String optionCode){ + // 缓存Key + String cacheKey = PREFIX_CODE + optionCode; + + // 先从缓存里拿 + OptionsModel model = CacheUtil.getTimed(OptionsModel.class, cacheKey); + if (model != null){ + return model; + } + + // 拿不到 -------- + // 防止缓存穿透判断 + boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey); + if(hasNilFlag){ + return null; + } + + try { + // 分布式加锁 + if(!DistributedLockUtil.lock(cacheKey)){ + // 无法申领分布式锁 + log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage()); + return null; + } + + // 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求 + model = CacheUtil.getTimed(OptionsModel.class, cacheKey); + if (model != null){ + return model; + } + + // 查询数据库 + ResultVo resultVo = optionsApi.getByCode(optionCode); + if(resultVo.isSuccess()){ + model = resultVo.getData(); + // 存入缓存 + CacheUtil.put(cacheKey, model); + } + }catch (Exception e){ + log.error(e.getMessage(),e); + }finally { + // 释放锁 + DistributedLockUtil.unlock(cacheKey); + } + + if(model == null){ + // 设置空变量 用于防止穿透判断 + CacheUtil.putNilFlag(cacheKey); + return null; + } + + return model; + } + + + // ============== 刷新缓存 ============== + + /** + * 刷新参数 - 删就完了 + * @param option + * @return + */ + public static boolean refreshOption(OptionsModel option){ + if(option == null || StringUtils.isEmpty(option.getOptionCode())){ + return true; + } + + // 计数器 + int count = 0; + + OptionsModel model = CacheUtil.getTimed(OptionsModel.class, PREFIX_CODE + option.getOptionCode()); + boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_CODE + option.getOptionCode()); + + // 只要不为空 则执行刷新 + if (hasNilFlag){ + count++; + // 清除空拦截 + boolean tmp = CacheUtil.delNilFlag(PREFIX_CODE + option.getOptionCode()); + if(tmp){ + count--; + } + } + + if(model != null){ + count++; + // 先删除 + boolean tmp = CacheUtil.del(PREFIX_CODE + option.getOptionCode()); + if(tmp){ + count--; + } + + // 发送通知消息 + redisPlugin.sendMessage( + OptionMsgFactory.createOptionMsg(option) + ); + } + + return count == 0; + } + + + + + // ===================================== + + @Autowired + public void setRedisPlugin(RedisPlugin redisPlugin) { + OptionsUtil.redisPlugin = redisPlugin; + } + + @Autowired + public void setOptionsApi(OptionsApi optionsApi) { + OptionsUtil.optionsApi = optionsApi; + } +} diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/entity/SysOptions.java b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/entity/SysOptions.java new file mode 100644 index 00000000..c6198f23 --- /dev/null +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/entity/SysOptions.java @@ -0,0 +1,51 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.opsli.modulars.system.options.entity; + + +import java.util.Date; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableLogic; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opsli.core.base.entity.BaseEntity; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.modulars.sys.options.entity + * @Author: Parker + * @CreateTime: 2021-02-07 18:24:38 + * @Description: 系统参数 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysOptions extends BaseEntity { + + /** 参数编号 */ + private String optionCode; + + /** 参数名称 */ + private String optionName; + + /** 参数值 */ + private String optionValue; + + // ======================================== + + +} diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/mapper/SysOptionsMapper.java b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/mapper/SysOptionsMapper.java new file mode 100644 index 00000000..f81a3244 --- /dev/null +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/mapper/SysOptionsMapper.java @@ -0,0 +1,39 @@ +/** +* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com +*

+* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy of +* the License at +*

+* http://www.apache.org/licenses/LICENSE-2.0 +*

+* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +package org.opsli.modulars.system.options.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import org.opsli.modulars.system.options.entity.SysOptions; + + +/** +* @BelongsProject: opsli-boot + +* @BelongsPackage: org.opsli.modulars.sys.options.mapper + +* @Author: Parker +* @CreateTime: 2021-02-07 18:24:38 +* @Description: 系统参数 Mapper +*/ +@Mapper +public interface SysOptionsMapper extends BaseMapper { + +} diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/mapper/xml/SysOptionsMapper.xml b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/mapper/xml/SysOptionsMapper.xml new file mode 100644 index 00000000..db38d253 --- /dev/null +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/mapper/xml/SysOptionsMapper.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/service/ISysOptionsService.java b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/service/ISysOptionsService.java new file mode 100644 index 00000000..b2ff5f54 --- /dev/null +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/service/ISysOptionsService.java @@ -0,0 +1,39 @@ +/** +* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com +*

+* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy of +* the License at +*

+* http://www.apache.org/licenses/LICENSE-2.0 +*

+* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +package org.opsli.modulars.system.options.service; + + +import org.opsli.core.base.service.interfaces.CrudServiceInterface; + + + +import org.opsli.modulars.system.options.entity.SysOptions; +import org.opsli.api.wrapper.system.options.OptionsModel; + + +/** +* @BelongsProject: opsli-boot + +* @BelongsPackage: org.opsli.modulars.sys.options.service + +* @Author: Parker +* @CreateTime: 2021-02-07 18:24:38 +* @Description: 系统参数 Service +*/ +public interface ISysOptionsService extends CrudServiceInterface { + +} diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/service/impl/SysOptionsServiceImpl.java b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/service/impl/SysOptionsServiceImpl.java new file mode 100644 index 00000000..6247159a --- /dev/null +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/service/impl/SysOptionsServiceImpl.java @@ -0,0 +1,172 @@ +/** +* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com +*

+* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy of +* the License at +*

+* http://www.apache.org/licenses/LICENSE-2.0 +*

+* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +package org.opsli.modulars.system.options.service.impl; + + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.commons.lang3.StringUtils; +import org.opsli.api.wrapper.system.options.OptionsModel; +import org.opsli.common.constants.MyBatisConstants; +import org.opsli.common.exception.ServiceException; +import org.opsli.core.base.service.impl.CrudServiceImpl; +import org.opsli.core.msg.CoreMsg; +import org.opsli.core.utils.OptionsUtil; +import org.opsli.modulars.system.SystemMsg; +import org.opsli.modulars.system.options.entity.SysOptions; +import org.opsli.modulars.system.options.mapper.SysOptionsMapper; +import org.opsli.modulars.system.options.service.ISysOptionsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.List; + + +/** +* @BelongsProject: opsli-boot + +* @BelongsPackage: org.opsli.modulars.sys.options.service.impl + +* @Author: Parker +* @CreateTime: 2021-02-07 18:24:38 +* @Description: 系统参数 Service Impl +*/ +@Service +public class SysOptionsServiceImpl extends CrudServiceImpl + implements ISysOptionsService { + + @Autowired(required = false) + private SysOptionsMapper mapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public OptionsModel insert(OptionsModel model) { + if(model == null){ + return null; + } + + // 唯一验证 + Integer count = this.uniqueVerificationByCode(model); + if(count != null && count > 0){ + // 重复 + throw new ServiceException(SystemMsg.EXCEPTION_OPTIONS_UNIQUE); + } + + return super.insert(model); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public OptionsModel update(OptionsModel model) { + if(model == null){ + return null; + } + + // 唯一验证 + Integer count = this.uniqueVerificationByCode(model); + if(count != null && count > 0){ + // 重复 + throw new ServiceException(SystemMsg.EXCEPTION_OPTIONS_UNIQUE); + } + + model = super.update(model); + if(model != null){ + // 清除缓存 + this.clearCache(Collections.singletonList(model)); + } + + return model; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean delete(String id) { + OptionsModel model = super.get(id); + boolean ret = super.delete(id); + + if(ret){ + // 清除缓存 + this.clearCache(Collections.singletonList(model)); + } + return ret; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteAll(String[] ids) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in(MyBatisConstants.FIELD_ID, Convert.toList(String.class, ids)); + List modelList = super.transformTs2Ms( + super.findList(queryWrapper) + ); + + // 清除缓存 + this.clearCache(modelList); + + return super.deleteAll(ids); + } + + // ======================= + + /** + * 唯一验证 + * @param model model + * @return Integer + */ + @Transactional(readOnly = true) + public Integer uniqueVerificationByCode(OptionsModel model){ + if(model == null){ + return null; + } + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("option_code", model.getOptionCode()); + + // 重复校验排除自身 + if(StringUtils.isNotEmpty(model.getId())){ + wrapper.notIn(MyBatisConstants.FIELD_ID, model.getId()); + } + + return super.count(wrapper); + } + + /** + * 清除缓存 + * @param optionList + */ + private void clearCache(List optionList){ + // 清空缓存 + if(CollUtil.isNotEmpty(optionList)){ + int cacheCount = 0; + for (OptionsModel model : optionList) { + cacheCount++; + boolean tmp = OptionsUtil.refreshOption(model); + if(tmp){ + cacheCount--; + } + } + // 判断删除状态 + if(cacheCount != 0){ + // 删除缓存失败 + throw new ServiceException(CoreMsg.CACHE_DEL_EXCEPTION); + } + } + } + +} diff --git a/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/web/SysOptionsRestController.java b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/web/SysOptionsRestController.java new file mode 100644 index 00000000..b44f7b48 --- /dev/null +++ b/opsli-modulars/opsli-modulars-system/src/main/java/org/opsli/modulars/system/options/web/SysOptionsRestController.java @@ -0,0 +1,230 @@ +/** +* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com +*

+* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy of +* the License at +*

+* http://www.apache.org/licenses/LICENSE-2.0 +*

+* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +package org.opsli.modulars.system.options.web; + + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ReflectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.opsli.api.base.result.ResultVo; +import org.opsli.api.web.system.options.OptionsApi; +import org.opsli.api.wrapper.system.options.OptionsModel; +import org.opsli.common.annotation.ApiRestController; +import org.opsli.common.annotation.EnableLog; +import org.opsli.common.annotation.RequiresPermissionsCus; +import org.opsli.common.utils.WrapperUtil; +import org.opsli.core.base.controller.BaseRestController; +import org.opsli.core.persistence.Page; +import org.opsli.core.persistence.querybuilder.QueryBuilder; +import org.opsli.core.persistence.querybuilder.WebQueryBuilder; +import org.opsli.modulars.system.options.entity.SysOptions; +import org.opsli.modulars.system.options.service.ISysOptionsService; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; + + +/** +* @BelongsProject: opsli-boot +* @BelongsPackage: org.opsli.modulars.sys.options.web +* @CreateTime: 2021-02-07 18:24:38 +* @Description: 系统参数 Controller +* +* @author Parker +*/ +@Api(tags = "系统参数") +@Slf4j +@ApiRestController("/sys/options") +public class SysOptionsRestController extends BaseRestController + implements OptionsApi { + + + /** + * 系统参数 查一条 + * @param model 模型 + * @return ResultVo + */ + @ApiOperation(value = "获得单条系统参数", notes = "获得单条系统参数 - ID") + @RequiresPermissions("sys_options_select") + @Override + public ResultVo get(OptionsModel model) { + // 如果系统内部调用 则直接查数据库 + if(model != null && model.getIzApi() != null && model.getIzApi()){ + model = IService.get(model); + } + return ResultVo.success(model); + } + + /** + * 系统参数 查询分页 + * @param pageNo 当前页 + * @param pageSize 每页条数 + * @param request request + * @return ResultVo + */ + @ApiOperation(value = "获得分页数据", notes = "获得分页数据 - 查询构造器") + @RequiresPermissions("sys_options_select") + @Override + public ResultVo findPage(Integer pageNo, Integer pageSize, HttpServletRequest request) { + + QueryBuilder queryBuilder = new WebQueryBuilder<>(entityClazz, request.getParameterMap()); + Page page = new Page<>(pageNo, pageSize); + page.setQueryWrapper(queryBuilder.build()); + page = IService.findPage(page); + + return ResultVo.success(page.getBootstrapData()); + } + + /** + * 系统参数 新增 + * @param model 模型 + * @return ResultVo + */ + @ApiOperation(value = "新增系统参数数据", notes = "新增系统参数数据") + @RequiresPermissions("sys_options_insert") + @EnableLog + @Override + public ResultVo insert(OptionsModel model) { + // 调用新增方法 + IService.insert(model); + return ResultVo.success("新增系统参数成功"); + } + + /** + * 系统参数 修改 + * @param model 模型 + * @return ResultVo + */ + @ApiOperation(value = "修改系统参数数据", notes = "修改系统参数数据") + @RequiresPermissions("sys_options_update") + @EnableLog + @Override + public ResultVo update(OptionsModel model) { + // 调用修改方法 + IService.update(model); + return ResultVo.success("修改系统参数成功"); + } + + + /** + * 系统参数 删除 + * @param id ID + * @return ResultVo + */ + @ApiOperation(value = "删除系统参数数据", notes = "删除系统参数数据") + @RequiresPermissions("sys_options_update") + @EnableLog + @Override + public ResultVo del(String id){ + IService.delete(id); + return ResultVo.success("删除系统参数成功"); + } + + /** + * 系统参数 批量删除 + * @param ids ID 数组 + * @return ResultVo + */ + @ApiOperation(value = "批量删除系统参数数据", notes = "批量删除系统参数数据") + @RequiresPermissions("sys_options_update") + @EnableLog + @Override + public ResultVo delAll(String ids){ + String[] idArray = Convert.toStrArray(ids); + IService.deleteAll(idArray); + return ResultVo.success("批量删除系统参数成功"); + } + + + /** + * 系统参数 Excel 导出 + * 注:这里 RequiresPermissionsCus 引入的是 自定义鉴权注解 + * + * 导出时,Token认证和方法权限认证 全部都由自定义完成 + * 因为在 导出不成功时,需要推送错误信息, + * 前端直接走下载流,当失败时无法获得失败信息,即使前后端换一种方式后端推送二进制文件前端再次解析也是最少2倍的耗时 + * ,且如果数据量过大,前端进行渲染时直接会把浏览器卡死 + * 而直接开启socket接口推送显然是太过浪费资源了,所以目前采用Java最原始的手段 + * response 推送 javascript代码 alert 提示报错信息 + * + * @param request request + * @param response response + */ + @ApiOperation(value = "导出Excel", notes = "导出Excel") + @RequiresPermissionsCus("sys_options_export") + @EnableLog + @Override + public void exportExcel(HttpServletRequest request, HttpServletResponse response) { + // 当前方法 + Method method = ReflectUtil.getMethodByName(this.getClass(), "exportExcel"); + QueryBuilder queryBuilder = new WebQueryBuilder<>(entityClazz, request.getParameterMap()); + super.excelExport(OptionsApi.TITLE, queryBuilder.build(), response, method); + } + + /** + * 系统参数 Excel 导入 + * 注:这里 RequiresPermissions 引入的是 Shiro原生鉴权注解 + * @param request 文件流 request + * @return ResultVo + */ + @ApiOperation(value = "导入Excel", notes = "导入Excel") + @RequiresPermissions("sys_options_import") + @EnableLog + @Override + public ResultVo importExcel(MultipartHttpServletRequest request) { + return super.importExcel(request); + } + + /** + * 系统参数 Excel 下载导入模版 + * 注:这里 RequiresPermissionsCus 引入的是 自定义鉴权注解 + * @param response response + */ + @ApiOperation(value = "导出Excel模版", notes = "导出Excel模版") + @RequiresPermissionsCus("sys_options_import") + @Override + public void importTemplate(HttpServletResponse response) { + // 当前方法 + Method method = ReflectUtil.getMethodByName(this.getClass(), "importTemplate"); + super.importTemplate(OptionsApi.TITLE, response, method); + } + + // ========================= + + /** + * 根据编号 获得参数配置 + * @param optionCode 参数编号 + * @return + */ + @Override + public ResultVo getByCode(String optionCode) { + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("option_code", optionCode); + SysOptions option = IService.getOne(wrapper); + + return ResultVo.success( + WrapperUtil.transformInstance(option, OptionsModel.class) + ); + } +}