package com.xy.service; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xy.annotate.Runners; import com.xy.collections.list.JArrayList; import com.xy.collections.list.JList; import com.xy.dto.SysDictDto; import com.xy.entity.SysDict; import com.xy.entity.SysDictLog; import com.xy.entity.SysDictRedis; import com.xy.error.CommRuntimeException; import com.xy.mapper.SysDictMapper; import com.xy.sys.EnumSysDictOperateType; import com.xy.utils.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import static com.xy.utils.Beans.copy; import static com.xy.utils.PlusBeans.toIPage; import static com.xy.utils.PlusBeans.toPageBean; /** *

* 字典表 服务实现类 *

* * @author lijin * @since 2022-12-09 */ @Slf4j @Service @AllArgsConstructor @Api(tags = "系统字典") public class SysDictServiceImpl extends ServiceImpl implements SysDictService { private static final String SYNC_DICT_URL = "https://api.mxrvending.com/sys/sys-dict/syncProd"; // private static final String SYNC_DICT_URL = "http://119.96.194.20:9050/sys/sys-dict/syncProd"; private RedisService redisService; private SysDictLogServiceImpl sysDictLogService; /** * post请求 * * @param url url * @param body spi参数 * @return {@link String} */ static String sendPost(String url, String body) { HttpResponse resultStr = HttpRequest.post(url) .header("Content-Type", "application/json") // 修改为 JSON 格式 .charset(CharsetUtil.UTF_8) .body(body) // 使用 JSON 字符串作为请求体 .timeout(30 * 1000) .execute(); String responseBody = resultStr.body(); if (resultStr.getStatus() != 200) { log.error("字典同步到生产环境异常: 状态码: {}, 响应体: {},appId:{},notifyId:{}", resultStr.getStatus(), responseBody); return "fail"; } return responseBody; } /** * 全量刷新redis字典数据 */ @Async public void refreshAll() { List SysDicts = this.list(); SysDicts.forEach(sysDict -> refuRedis(sysDict)); } /** * 同步到生产环境,处理接收到的字典数据 nacos加白名单 * * @param syncProd 目标字典数据 * @return 响应结果 */ @ApiOperation("同步到生产环境-接收处理") @PostMapping("syncProd") public R syncProd(@RequestBody SysDictDto.SyncProd syncProd) { log.info("字典同步数据: {}", JSONUtil.toJsonPrettyStr(syncProd)); String paterCode = syncProd.getPaterCode(); String code = syncProd.getCode(); Integer operateType = syncProd.getOperateType(); if (!SysDictDto.SYNC_PWD.equals(syncProd.getPwd())) { throw new CommRuntimeException("非法请求!"); } try { if (StrUtil.isEmpty(paterCode)) { //根字典 handleRootDict(syncProd, code, operateType); } else { //子字典 handleChildDict(syncProd, paterCode, operateType); } } catch (Exception e) { log.error("同步到生产环境失败", e); return R.fail("同步失败,请稍后重试"); } return R.ok(); } /** * 处理根字典的操作,根据操作类型决定增加、更新或删除 * * @param syncProd 目标字典数据 * @param code 字典代码 * @param operateType 操作类型 */ private void handleRootDict(SysDictDto.SyncProd syncProd, String code, Integer operateType) { switch (operateType) { case 1: addRootDict(syncProd, code); break; case 2: updateRootDict(syncProd); break; case 3: deleteRootDict(syncProd.getCode()); break; default: log.warn("未知操作类型: {}", operateType); } } /** * 处理子字典的操作,根据操作类型决定增加、更新或删除 * * @param syncProd 目标字典数据 * @param paterCode 父级字典代码 * @param operateType 操作类型 */ private void handleChildDict(SysDictDto.SyncProd syncProd, String paterCode, Integer operateType) { switch (operateType) { case 1: addChildDict(syncProd, paterCode); break; case 2: updateChildDict(syncProd, paterCode); break; case 3: deleteChildDict(syncProd, paterCode); break; default: log.warn("未知操作类型: {}", operateType); } } /** * 添加根字典,如果字典代码不存在则创建新的字典 * * @param syncProd 目标字典数据 * @param code 字典代码 */ private void addRootDict(SysDictDto.SyncProd syncProd, String code) { long count = count(new LambdaQueryWrapper().eq(SysDict::getCode, code)); if (count <= 0) { SysDict sysDict = createSysDict(syncProd); save(sysDict); refuRedis(sysDict); } } /** * 更新根字典,若不存在则调用添加方法 * * @param syncProd 目标字典数据 */ private void updateRootDict(SysDictDto.SyncProd syncProd) { SysDict sysP = getOne(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getCode, syncProd.getCode())); if (sysP == null) { addRootDict(syncProd, syncProd.getCode()); } else { updateSysDict(sysP, syncProd); } } /** * 删除根字典及其相关的子字典 * * @param code 字典代码 */ private void deleteRootDict(String code) { List sysDicts = getRelatedDicts(code); remove(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getCode, code)); remove(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getPaterCode, code)); sysDicts.forEach(this::refuRedis); } /** * 添加子字典,如果代码不存在则创建新的字典 * * @param syncProd 目标字典数据 * @param paterCode 父级字典代码 */ private void addChildDict(SysDictDto.SyncProd syncProd, String paterCode) { long count = count(new LambdaQueryWrapper().eq(SysDict::getCode, syncProd.getCode()).eq(SysDict::getPaterCode, paterCode)); if (count <= 0) { SysDict sysDict = createSysDict(syncProd); save(sysDict); refuRedis(sysDict); } } /** * 更新子字典,若不存在则调用添加方法 * * @param syncProd 目标字典数据 * @param paterCode 父级字典代码 */ private void updateChildDict(SysDictDto.SyncProd syncProd, String paterCode) { SysDict sysP = getOne(Wrappers.lambdaQuery(SysDict.class) .eq(SysDict::getCode, syncProd.getCode()) .eq(SysDict::getPaterCode, paterCode)); if (sysP == null) { addChildDict(syncProd, paterCode); } else { updateSysDict(sysP, syncProd); } } /** * 删除子字典 * * @param syncProd 目标字典数据 * @param paterCode 父级字典代码 */ private void deleteChildDict(SysDictDto.SyncProd syncProd, String paterCode) { List list = new ArrayList<>(); SysDict sysDict = getOne(Wrappers.lambdaQuery(SysDict.class) .eq(SysDict::getCode, syncProd.getCode()) .eq(SysDict::getPaterCode, paterCode)); if (sysDict != null) { list.add(sysDict); remove(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getPaterCode, syncProd.getCode()).eq(SysDict::getCode, syncProd.getCode())); list.forEach(this::refuRedis); } } /** * 创建新的字典对象 * * @param syncProd 目标字典数据 * @return 新创建的字典对象 */ private SysDict createSysDict(SysDictDto.SyncProd syncProd) { SysDict sysDict = BeanUtil.copyProperties(syncProd, SysDict.class); sysDict.setCreateTime(LocalDateTime.now()); sysDict.setCreateUser(1L); return sysDict; } /** * 更新字典对象的属性 * * @param sysDict 需要更新的字典对象 * @param syncProd 目标字典数据 */ private void updateSysDict(SysDict sysDict, SysDictDto.SyncProd syncProd) { sysDict.setValue(syncProd.getValue()); sysDict.setMsg(syncProd.getMsg()); sysDict.setUpdateTime(LocalDateTime.now()); updateById(sysDict); refuRedis(sysDict); } /** * 根据字典代码获取相关的字典 * * @param code 字典代码 * @return 相关字典列表 */ private List getRelatedDicts(String code) { List list = new ArrayList<>(); SysDict sysDict = getOne(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getCode, code)); if (sysDict != null) { list.add(sysDict); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getPaterCode, code); List sysDicts = list(lqw); if (CollUtil.isNotEmpty(sysDicts)) { list.addAll(sysDicts); } } return list; } @ApiOperation("手动同步到生产环境") @PostMapping("syncToProd") public R syncToProd(@RequestBody @Validated SysDictDto.SyncToProd syncToProd) { Long id = syncToProd.getId(); SysDictLog sysDictLog = sysDictLogService.getById(id); // 确保获取到的字典日志不为空 if (sysDictLog == null) { return R.fail("字典日志不存在"); } Boolean isSync = sysDictLog.getIsSync(); if (BooleanUtil.isTrue(isSync)) { return R.fail("已同步,请勿重复操作"); } Integer sysDictId = sysDictLog.getSysDictId(); SysDict sysDict = getById(sysDictId); SysDictDto.SyncProd syncProd = BeanUtil.copyProperties(sysDictLog, SysDictDto.SyncProd.class); syncProd.setValue(sysDictLog.getValue()); if (sysDict != null) { //非删除 syncProd.setCssClass(sysDict.getCssClass()); syncProd.setListClass(sysDict.getListClass()); syncProd.setOrders(sysDict.getOrders()); } syncProd.setPwd(SysDictDto.SYNC_PWD); String body = JSONUtil.toJsonStr(syncProd); log.info("字典同步数据: {}", body); String res = sendPost(SYNC_DICT_URL, body); if ("fail".equals(res)) { return R.fail("同步失败,请稍后重试"); } else { sysDictLog.setIsSync(true); sysDictLogService.updateById(sysDictLog); } return R.ok(); } @ApiOperation("添加") @PostMapping("save") public R save(@RequestBody @Validated SysDictDto.Save save) { //添加字典 long count = !Emptys.check(save.getPaterCode()) ? count(new LambdaQueryWrapper().eq(SysDict::getCode, save.getCode())) : count(new LambdaQueryWrapper().eq(SysDict::getCode, save.getCode()).eq(SysDict::getPaterCode, save.getPaterCode())); if (count > 0) { return R.fail("code已存在"); } SysDict sysDict = copy(SysDict.class, save) .createUserTime(AuthorizeUtils.getLoginId(Long.class)); save(sysDict); //日志 SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class); sysDictLogService.handleLog(sysDict, sysDictLog, EnumSysDictOperateType.ADD); //刷新redis refuRedis(sysDict); return R.ok(); } @ApiOperation("修改") @PostMapping("update") public R update(@RequestBody @Validated SysDictDto.Update update) { SysDict sysDict = getById(update.getId()); if (!sysDict.getCode().equals(update.getCode())) { long count = !Emptys.check(update.getPaterCode()) ? count(new LambdaQueryWrapper().eq(SysDict::getCode, update.getCode())) : count(new LambdaQueryWrapper().eq(SysDict::getCode, update.getCode()).eq(SysDict::getPaterCode, update.getPaterCode())); if (count > 0) { return R.fail("code已存在"); } } //如果根字典code修改,则 子字典也要记录日志(作为新增) if (StrUtil.isEmpty(sysDict.getPaterCode()) && !sysDict.getCode().equals(update.getCode())) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper().eq(SysDict::getPaterCode, sysDict.getCode()); List sysDicts = list(lambdaQueryWrapper); if (CollUtil.isNotEmpty(sysDicts)) { //存在子字典,修改日志记录 sysDicts.forEach(sd -> { sd.setPaterCode(update.getCode()); SysDictLog sysDictLog = BeanUtil.copyProperties(sd, SysDictLog.class); sysDictLogService.handleLog(sysDict, sysDictLog, EnumSysDictOperateType.ADD); }); } } //修改字典 SysDict obj = copy(SysDict.class, update) .update(AuthorizeUtils.getLoginId(Long.class)); updateById(obj); //修改子字典 update(new SysDict().setPaterCode(obj.getCode()), new LambdaQueryWrapper().eq(SysDict::getPaterCode, sysDict.getCode())); //日志 SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class); sysDictLogService.handleLog(sysDict, sysDictLog, EnumSysDictOperateType.UPDATE); //刷新redis refuRedis(obj); return R.ok(); } @ApiOperation("删除") @PostMapping("del") @Transactional(rollbackFor = Exception.class) public R del(@RequestBody @Validated SysDictDto.Del del) { //删除字典 List ids = del.getId(); List list = list(new LambdaQueryWrapper().in(SysDict::getId, ids)); removeBatchByIds(ids); //日志 if (CollUtil.isNotEmpty(list)) { list.forEach(sysDict -> { SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class); sysDictLogService.handleLog(sysDict, sysDictLog, EnumSysDictOperateType.DEL); }); } //删除子字典 JList paters = new JArrayList<>(list).filter().isNull(SysDict::getPaterCode).list(); JList paters2 = new JArrayList<>(list).filter().eq(SysDict::getPaterCode, "").list(); if (Emptys.check(paters2)) { paters.addAll(paters2); } if (Emptys.check(paters)) { JList codes = paters.getProperty(SysDict::getCode); LambdaQueryWrapper lqw = new LambdaQueryWrapper().in(SysDict::getPaterCode, codes); List sysDicts = list(lqw); if (CollUtil.isNotEmpty(sysDicts)) { removeBatchByIds(sysDicts); sysDicts.forEach(sysDict -> { SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class); sysDictLogService.handleLog(sysDict, sysDictLog, EnumSysDictOperateType.DEL); }); } } //刷新redis list.forEach(sysDict -> refuRedis(sysDict)); return R.ok(); } @ApiOperation("分页查询") @PostMapping("page") public R> page(@RequestBody SysDictDto.Page pageInfo) { PageBean page = pageInfo.getPage(); LambdaQueryWrapper lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(pageInfo, SysDict.class) .ge(SysDict::getCreateTime, pageInfo.getBeginCreateTime()) .le(SysDict::getCreateTime, pageInfo.getEndCreateTime()) .like(SysDict::getMsg) .like(SysDict::getValue) .build() .orderByAsc(!Emptys.check(page.getOrders()), SysDict::getOrders) .and(StringUtils.isEmpty(pageInfo.getPaterCode()), sysDictLambdaQueryWrapper -> sysDictLambdaQueryWrapper .isNull(SysDict::getPaterCode) .or() .eq(SysDict::getPaterCode, "") ); IPage iPage = page(toIPage(page), lambdaQueryWrapper); return R.ok(toPageBean(SysDictDto.Vo.class, iPage)); } @ApiOperation("集合查询") @PostMapping("list") public R> list(@RequestHeader(value = "lang", required = false) String lang, @RequestBody SysDictDto.SelectList selectList) { LambdaQueryWrapper lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(selectList, SysDict.class) .in(SysDict::getId, selectList.getIds()) .in(SysDict::getCode, selectList.getCodes()) .in(SysDict::getPaterCode, selectList.getPaterCodes()) .like(SysDict::getMsg) .like(SysDict::getValue) .build() .orderByAsc(SysDict::getOrders); List list = list(lambdaQueryWrapper); List voList = copy(SysDictDto.Vo.class, list); // if ("en".equals(lang)) { // //英文菜单 // voList.forEach(v -> { // String enName = v.getEnMsg(); // if (StrUtil.isNotEmpty(enName)) { // v.setMsg(enName); // } // }); // // } return R.ok(voList); } @ApiOperation("集合查询(无需授权)") @PostMapping("list2") public R> list2(@RequestHeader(value = "lang", required = false) String lang, @RequestBody SysDictDto.SelectList selectList) { return list(lang, selectList); } /** * 刷新redis * * @param sysDict */ public void refuRedis(SysDict sysDict) { List list; String key = sysDict.getPaterCode(); if (!Emptys.check(key)) { //父字典 list = list(new LambdaQueryWrapper().eq(SysDict::getPaterCode, sysDict.getCode()).eq(SysDict::getStatus, true)); key = sysDict.getCode(); } else { //子字典 list = list(new LambdaQueryWrapper().eq(SysDict::getPaterCode, key).eq(SysDict::getStatus, true)); } redisService.removeMap(SysDictUtils.getKey(key)); for (SysDict dict : list) { redisService.setMap(SysDictUtils.getKey(key), dict.getCode(), copy(SysDictRedis.class, dict)); } log.info("字典刷新redis完成~"); } /** * 全量加载字典到redis */ @Runners public void loadRedis() { List sysDicts = list(new LambdaQueryWrapper().eq(SysDict::getStatus, true)); List sysDictRedis = Beans.copy(SysDictRedis.class, sysDicts); JList list = new JArrayList<>(sysDictRedis); //父字典 JList paters = list.filter().isNull(SysDictRedis::getPaterCode).list(); JList paters2 = list.filter().eq(SysDictRedis::getPaterCode, "").list(); if (Emptys.check(paters2)) { paters.addAll(paters2); } redisService.removeLikeMap(SysDictUtils.SYS_DICT_REDIS_PREFIX); //[父code -> 子列表]数据结构写入redis paters.forEach(sysDict -> { JList sysDictJList = list.filter().eq(SysDictRedis::getPaterCode, sysDict.getCode()).list(); sysDictJList.forEach(sysDictRediss -> redisService.setMap(SysDictUtils.getKey(sysDict.getCode()), sysDictRediss.getCode(), sysDictRediss)); }); log.info("字典写入redis完成~"); } }