SysDictServiceImpl.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. package com.xy.service;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.collection.CollUtil;
  4. import cn.hutool.core.util.CharsetUtil;
  5. import cn.hutool.core.util.StrUtil;
  6. import cn.hutool.http.HttpRequest;
  7. import cn.hutool.http.HttpResponse;
  8. import cn.hutool.json.JSONUtil;
  9. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  10. import com.baomidou.mybatisplus.core.metadata.IPage;
  11. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  12. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  13. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  14. import com.xy.annotate.Runners;
  15. import com.xy.collections.list.JArrayList;
  16. import com.xy.collections.list.JList;
  17. import com.xy.dto.SysDictDto;
  18. import com.xy.entity.SysDict;
  19. import com.xy.entity.SysDictLog;
  20. import com.xy.entity.SysDictRedis;
  21. import com.xy.error.CommRuntimeException;
  22. import com.xy.mapper.SysDictMapper;
  23. import com.xy.sys.EnumSysDictOperateType;
  24. import com.xy.utils.*;
  25. import io.swagger.annotations.Api;
  26. import io.swagger.annotations.ApiOperation;
  27. import lombok.AllArgsConstructor;
  28. import lombok.extern.slf4j.Slf4j;
  29. import org.springframework.scheduling.annotation.Async;
  30. import org.springframework.stereotype.Service;
  31. import org.springframework.transaction.annotation.Transactional;
  32. import org.springframework.validation.annotation.Validated;
  33. import org.springframework.web.bind.annotation.PostMapping;
  34. import org.springframework.web.bind.annotation.RequestBody;
  35. import org.springframework.web.bind.annotation.RequestHeader;
  36. import java.time.LocalDateTime;
  37. import java.util.ArrayList;
  38. import java.util.List;
  39. import static com.xy.utils.Beans.copy;
  40. import static com.xy.utils.PlusBeans.toIPage;
  41. import static com.xy.utils.PlusBeans.toPageBean;
  42. /**
  43. * <p>
  44. * 字典表 服务实现类
  45. * </p>
  46. *
  47. * @author lijin
  48. * @since 2022-12-09
  49. */
  50. @Slf4j
  51. @Service
  52. @AllArgsConstructor
  53. @Api(tags = "系统字典")
  54. public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> implements SysDictService {
  55. private static final String SYNC_DICT_URL = "https://api.mxrvending.com/sys/sys-dict/syncProd";
  56. // private static final String SYNC_DICT_URL = "http://119.96.194.20:9050/sys/sys-dict/syncProd";
  57. private RedisService<SysDictRedis> redisService;
  58. private SysDictLogServiceImpl sysDictLogService;
  59. /**
  60. * post请求
  61. *
  62. * @param url url
  63. * @param body spi参数
  64. * @return {@link String}
  65. */
  66. static String sendPost(String url, String body) {
  67. HttpResponse resultStr = HttpRequest.post(url)
  68. .header("Content-Type", "application/json") // 修改为 JSON 格式
  69. .charset(CharsetUtil.UTF_8)
  70. .body(body) // 使用 JSON 字符串作为请求体
  71. .timeout(30 * 1000)
  72. .execute();
  73. String responseBody = resultStr.body();
  74. if (resultStr.getStatus() != 200) {
  75. log.error("字典同步到生产环境异常: 状态码: {}, 响应体: {},appId:{},notifyId:{}", resultStr.getStatus(), responseBody);
  76. return "fail";
  77. }
  78. return responseBody;
  79. }
  80. /**
  81. * 全量刷新redis字典数据
  82. */
  83. @Async
  84. public void refreshAll() {
  85. List<SysDict> SysDicts = this.list();
  86. SysDicts.forEach(sysDict -> refuRedis(sysDict));
  87. }
  88. /**
  89. * 同步到生产环境,处理接收到的字典数据 nacos加白名单
  90. *
  91. * @param syncProd 目标字典数据
  92. * @return 响应结果
  93. */
  94. @ApiOperation("同步到生产环境-接收处理")
  95. @PostMapping("syncProd")
  96. public R syncProd(@RequestBody SysDictDto.SyncProd syncProd) {
  97. log.info("字典同步数据: {}", JSONUtil.toJsonPrettyStr(syncProd));
  98. String paterCode = syncProd.getPaterCode();
  99. String code = syncProd.getCode();
  100. Integer operateType = syncProd.getOperateType();
  101. if (SysDictDto.SYNC_PWD.equals(syncProd.getPwd())) {
  102. throw new CommRuntimeException("非法请求!");
  103. }
  104. try {
  105. if (StrUtil.isEmpty(paterCode)) {
  106. //根字典
  107. handleRootDict(syncProd, code, operateType);
  108. } else {
  109. //子字典
  110. handleChildDict(syncProd, paterCode, operateType);
  111. }
  112. } catch (Exception e) {
  113. log.error("同步到生产环境失败", e);
  114. return R.fail("同步失败,请稍后重试");
  115. }
  116. return R.ok();
  117. }
  118. /**
  119. * 处理根字典的操作,根据操作类型决定增加、更新或删除
  120. *
  121. * @param syncProd 目标字典数据
  122. * @param code 字典代码
  123. * @param operateType 操作类型
  124. */
  125. private void handleRootDict(SysDictDto.SyncProd syncProd, String code, Integer operateType) {
  126. switch (operateType) {
  127. case 1:
  128. addRootDict(syncProd, code);
  129. break;
  130. case 2:
  131. updateRootDict(syncProd);
  132. break;
  133. case 3:
  134. deleteRootDict(syncProd.getCode());
  135. break;
  136. default:
  137. log.warn("未知操作类型: {}", operateType);
  138. }
  139. }
  140. /**
  141. * 处理子字典的操作,根据操作类型决定增加、更新或删除
  142. *
  143. * @param syncProd 目标字典数据
  144. * @param paterCode 父级字典代码
  145. * @param operateType 操作类型
  146. */
  147. private void handleChildDict(SysDictDto.SyncProd syncProd, String paterCode, Integer operateType) {
  148. switch (operateType) {
  149. case 1:
  150. addChildDict(syncProd, paterCode);
  151. break;
  152. case 2:
  153. updateChildDict(syncProd, paterCode);
  154. break;
  155. case 3:
  156. deleteChildDict(syncProd, paterCode);
  157. break;
  158. default:
  159. log.warn("未知操作类型: {}", operateType);
  160. }
  161. }
  162. /**
  163. * 添加根字典,如果字典代码不存在则创建新的字典
  164. *
  165. * @param syncProd 目标字典数据
  166. * @param code 字典代码
  167. */
  168. private void addRootDict(SysDictDto.SyncProd syncProd, String code) {
  169. long count = count(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, code));
  170. if (count <= 0) {
  171. SysDict sysDict = createSysDict(syncProd);
  172. save(sysDict);
  173. refuRedis(sysDict);
  174. }
  175. }
  176. /**
  177. * 更新根字典,若不存在则调用添加方法
  178. *
  179. * @param syncProd 目标字典数据
  180. */
  181. private void updateRootDict(SysDictDto.SyncProd syncProd) {
  182. SysDict sysP = getOne(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getCode, syncProd.getCode()));
  183. if (sysP == null) {
  184. addRootDict(syncProd, syncProd.getCode());
  185. } else {
  186. updateSysDict(sysP, syncProd);
  187. }
  188. }
  189. /**
  190. * 删除根字典及其相关的子字典
  191. *
  192. * @param code 字典代码
  193. */
  194. private void deleteRootDict(String code) {
  195. List<SysDict> sysDicts = getRelatedDicts(code);
  196. remove(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getCode, code));
  197. remove(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getPaterCode, code));
  198. sysDicts.forEach(this::refuRedis);
  199. }
  200. /**
  201. * 添加子字典,如果代码不存在则创建新的字典
  202. *
  203. * @param syncProd 目标字典数据
  204. * @param paterCode 父级字典代码
  205. */
  206. private void addChildDict(SysDictDto.SyncProd syncProd, String paterCode) {
  207. long count = count(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, syncProd.getCode()).eq(SysDict::getPaterCode, paterCode));
  208. if (count <= 0) {
  209. SysDict sysDict = createSysDict(syncProd);
  210. save(sysDict);
  211. refuRedis(sysDict);
  212. }
  213. }
  214. /**
  215. * 更新子字典,若不存在则调用添加方法
  216. *
  217. * @param syncProd 目标字典数据
  218. * @param paterCode 父级字典代码
  219. */
  220. private void updateChildDict(SysDictDto.SyncProd syncProd, String paterCode) {
  221. SysDict sysP = getOne(Wrappers.lambdaQuery(SysDict.class)
  222. .eq(SysDict::getCode, syncProd.getCode())
  223. .eq(SysDict::getPaterCode, paterCode));
  224. if (sysP == null) {
  225. addChildDict(syncProd, paterCode);
  226. } else {
  227. updateSysDict(sysP, syncProd);
  228. }
  229. }
  230. /**
  231. * 删除子字典
  232. *
  233. * @param syncProd 目标字典数据
  234. * @param paterCode 父级字典代码
  235. */
  236. private void deleteChildDict(SysDictDto.SyncProd syncProd, String paterCode) {
  237. List<SysDict> list = new ArrayList<>();
  238. SysDict sysDict = getOne(Wrappers.lambdaQuery(SysDict.class)
  239. .eq(SysDict::getCode, syncProd.getCode())
  240. .eq(SysDict::getPaterCode, paterCode));
  241. if (sysDict != null) {
  242. list.add(sysDict);
  243. remove(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getPaterCode, syncProd.getCode()).eq(SysDict::getCode, syncProd.getCode()));
  244. list.forEach(this::refuRedis);
  245. }
  246. }
  247. /**
  248. * 创建新的字典对象
  249. *
  250. * @param syncProd 目标字典数据
  251. * @return 新创建的字典对象
  252. */
  253. private SysDict createSysDict(SysDictDto.SyncProd syncProd) {
  254. SysDict sysDict = BeanUtil.copyProperties(syncProd, SysDict.class);
  255. sysDict.setCreateTime(LocalDateTime.now());
  256. sysDict.setCreateUser(1L);
  257. return sysDict;
  258. }
  259. /**
  260. * 更新字典对象的属性
  261. *
  262. * @param sysDict 需要更新的字典对象
  263. * @param syncProd 目标字典数据
  264. */
  265. private void updateSysDict(SysDict sysDict, SysDictDto.SyncProd syncProd) {
  266. sysDict.setValue(syncProd.getValue());
  267. sysDict.setMsg(syncProd.getMsg());
  268. sysDict.setUpdateTime(LocalDateTime.now());
  269. updateById(sysDict);
  270. refuRedis(sysDict);
  271. }
  272. /**
  273. * 根据字典代码获取相关的字典
  274. *
  275. * @param code 字典代码
  276. * @return 相关字典列表
  277. */
  278. private List<SysDict> getRelatedDicts(String code) {
  279. List<SysDict> list = new ArrayList<>();
  280. SysDict sysDict = getOne(Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getCode, code));
  281. if (sysDict != null) {
  282. list.add(sysDict);
  283. LambdaQueryWrapper<SysDict> lqw = Wrappers.lambdaQuery(SysDict.class).eq(SysDict::getPaterCode, code);
  284. List<SysDict> sysDicts = list(lqw);
  285. if (CollUtil.isNotEmpty(sysDicts)) {
  286. list.addAll(sysDicts);
  287. }
  288. }
  289. return list;
  290. }
  291. @ApiOperation("手动同步到生产环境")
  292. @PostMapping("syncToProd")
  293. public R syncToProd(@RequestBody @Validated SysDictDto.SyncToProd syncToProd) {
  294. Long id = syncToProd.getId();
  295. SysDictLog sysDictLog = sysDictLogService.getById(id);
  296. // 确保获取到的字典日志不为空
  297. if (sysDictLog == null) {
  298. return R.fail("字典日志不存在");
  299. }
  300. SysDictDto.SyncProd syncProd = BeanUtil.copyProperties(sysDictLog, SysDictDto.SyncProd.class);
  301. String res = sendPost(SYNC_DICT_URL, JSONUtil.toJsonStr(syncProd));
  302. if ("fail".equals(res)) {
  303. return R.fail("同步失败,请稍后重试");
  304. } else {
  305. sysDictLog.setIsSync(true);
  306. sysDictLogService.updateById(sysDictLog);
  307. }
  308. return R.ok();
  309. }
  310. @ApiOperation("添加")
  311. @PostMapping("save")
  312. public R save(@RequestBody @Validated SysDictDto.Save save) {
  313. //添加字典
  314. long count = !Emptys.check(save.getPaterCode()) ? count(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, save.getCode()))
  315. : count(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, save.getCode()).eq(SysDict::getPaterCode, save.getPaterCode()));
  316. if (count > 0) {
  317. return R.fail("code已存在");
  318. }
  319. SysDict sysDict = copy(SysDict.class, save)
  320. .createUserTime(AuthorizeUtils.getLoginId(Long.class));
  321. save(sysDict);
  322. //日志
  323. SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class);
  324. sysDictLogService.handleLog(sysDictLog, EnumSysDictOperateType.ADD);
  325. //刷新redis
  326. refuRedis(sysDict);
  327. return R.ok();
  328. }
  329. @ApiOperation("修改")
  330. @PostMapping("update")
  331. public R update(@RequestBody @Validated SysDictDto.Update update) {
  332. SysDict sysDict = getById(update.getId());
  333. if (!sysDict.getCode().equals(update.getCode())) {
  334. long count = !Emptys.check(update.getPaterCode()) ? count(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, update.getCode()))
  335. : count(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, update.getCode()).eq(SysDict::getPaterCode, update.getPaterCode()));
  336. if (count > 0) {
  337. return R.fail("code已存在");
  338. }
  339. }
  340. //如果根字典code修改,则 子字典也要记录日志(作为新增)
  341. if (StrUtil.isEmpty(sysDict.getPaterCode()) && !sysDict.getCode().equals(update.getCode())) {
  342. LambdaQueryWrapper<SysDict> lambdaQueryWrapper = new LambdaQueryWrapper<SysDict>().eq(SysDict::getPaterCode, sysDict.getCode());
  343. List<SysDict> sysDicts = list(lambdaQueryWrapper);
  344. if (CollUtil.isNotEmpty(sysDicts)) {
  345. //存在子字典,修改日志记录
  346. sysDicts.forEach(sd -> {
  347. sd.setPaterCode(update.getCode());
  348. SysDictLog sysDictLog = BeanUtil.copyProperties(sd, SysDictLog.class);
  349. sysDictLogService.handleLog(sysDictLog, EnumSysDictOperateType.ADD);
  350. });
  351. }
  352. }
  353. //修改字典
  354. SysDict obj = copy(SysDict.class, update)
  355. .update(AuthorizeUtils.getLoginId(Long.class));
  356. updateById(obj);
  357. //修改子字典
  358. update(new SysDict().setPaterCode(obj.getCode()), new LambdaQueryWrapper<SysDict>().eq(SysDict::getPaterCode, sysDict.getCode()));
  359. //日志
  360. SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class);
  361. sysDictLogService.handleLog(sysDictLog, EnumSysDictOperateType.UPDATE);
  362. //刷新redis
  363. refuRedis(obj);
  364. return R.ok();
  365. }
  366. @ApiOperation("删除")
  367. @PostMapping("del")
  368. @Transactional(rollbackFor = Exception.class)
  369. public R del(@RequestBody @Validated SysDictDto.Del del) {
  370. //删除字典
  371. List<Integer> ids = del.getId();
  372. List<SysDict> list = list(new LambdaQueryWrapper<SysDict>().in(SysDict::getId, ids));
  373. removeBatchByIds(ids);
  374. //日志
  375. if (CollUtil.isNotEmpty(list)) {
  376. list.forEach(sysDict -> {
  377. SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class);
  378. sysDictLogService.handleLog(sysDictLog, EnumSysDictOperateType.DEL);
  379. });
  380. }
  381. //删除子字典
  382. JList<SysDict> paters = new JArrayList<>(list).filter().isNull(SysDict::getPaterCode).list();
  383. JList<SysDict> paters2 = new JArrayList<>(list).filter().eq(SysDict::getPaterCode, "").list();
  384. if (Emptys.check(paters2)) {
  385. paters.addAll(paters2);
  386. }
  387. if (Emptys.check(paters)) {
  388. JList<String> codes = paters.getProperty(SysDict::getCode);
  389. LambdaQueryWrapper<SysDict> lqw = new LambdaQueryWrapper<SysDict>().in(SysDict::getPaterCode, codes);
  390. List<SysDict> sysDicts = list(lqw);
  391. if (CollUtil.isNotEmpty(sysDicts)) {
  392. removeBatchByIds(sysDicts);
  393. sysDicts.forEach(sysDict -> {
  394. SysDictLog sysDictLog = BeanUtil.copyProperties(sysDict, SysDictLog.class);
  395. sysDictLogService.handleLog(sysDictLog, EnumSysDictOperateType.DEL);
  396. });
  397. }
  398. }
  399. //刷新redis
  400. list.forEach(sysDict -> refuRedis(sysDict));
  401. return R.ok();
  402. }
  403. @ApiOperation("分页查询")
  404. @PostMapping("page")
  405. public R<PageBean<SysDictDto.Vo>> page(@RequestBody SysDictDto.Page pageInfo) {
  406. PageBean<SysDict> page = pageInfo.getPage();
  407. LambdaQueryWrapper<SysDict> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(pageInfo, SysDict.class)
  408. .ge(SysDict::getCreateTime, pageInfo.getBeginCreateTime())
  409. .le(SysDict::getCreateTime, pageInfo.getEndCreateTime())
  410. .like(SysDict::getMsg)
  411. .like(SysDict::getValue)
  412. .build()
  413. .orderByAsc(!Emptys.check(page.getOrders()), SysDict::getOrders)
  414. .and(StringUtils.isEmpty(pageInfo.getPaterCode()), sysDictLambdaQueryWrapper -> sysDictLambdaQueryWrapper
  415. .isNull(SysDict::getPaterCode)
  416. .or()
  417. .eq(SysDict::getPaterCode, "")
  418. );
  419. IPage<SysDict> iPage = page(toIPage(page), lambdaQueryWrapper);
  420. return R.ok(toPageBean(SysDictDto.Vo.class, iPage));
  421. }
  422. @ApiOperation("集合查询")
  423. @PostMapping("list")
  424. public R<List<SysDictDto.Vo>> list(@RequestHeader(value = "lang", required = false) String lang, @RequestBody SysDictDto.SelectList selectList) {
  425. LambdaQueryWrapper<SysDict> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(selectList, SysDict.class)
  426. .in(SysDict::getId, selectList.getIds())
  427. .in(SysDict::getCode, selectList.getCodes())
  428. .in(SysDict::getPaterCode, selectList.getPaterCodes())
  429. .like(SysDict::getMsg)
  430. .like(SysDict::getValue)
  431. .build()
  432. .orderByAsc(SysDict::getOrders);
  433. List<SysDict> list = list(lambdaQueryWrapper);
  434. List<SysDictDto.Vo> voList = copy(SysDictDto.Vo.class, list);
  435. // if ("en".equals(lang)) {
  436. // //英文菜单
  437. // voList.forEach(v -> {
  438. // String enName = v.getEnMsg();
  439. // if (StrUtil.isNotEmpty(enName)) {
  440. // v.setMsg(enName);
  441. // }
  442. // });
  443. //
  444. // }
  445. return R.ok(voList);
  446. }
  447. @ApiOperation("集合查询(无需授权)")
  448. @PostMapping("list2")
  449. public R<List<SysDictDto.Vo>> list2(@RequestHeader(value = "lang", required = false) String lang, @RequestBody SysDictDto.SelectList selectList) {
  450. return list(lang, selectList);
  451. }
  452. /**
  453. * 刷新redis
  454. *
  455. * @param sysDict
  456. */
  457. public void refuRedis(SysDict sysDict) {
  458. List<SysDict> list;
  459. String key = sysDict.getPaterCode();
  460. if (!Emptys.check(key)) {
  461. //父字典
  462. list = list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getPaterCode, sysDict.getCode()).eq(SysDict::getStatus, true));
  463. key = sysDict.getCode();
  464. } else {
  465. //子字典
  466. list = list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getPaterCode, key).eq(SysDict::getStatus, true));
  467. }
  468. redisService.removeMap(SysDictUtils.getKey(key));
  469. for (SysDict dict : list) {
  470. redisService.setMap(SysDictUtils.getKey(key), dict.getCode(), copy(SysDictRedis.class, dict));
  471. }
  472. log.info("字典刷新redis完成~");
  473. }
  474. /**
  475. * 全量加载字典到redis
  476. */
  477. @Runners
  478. public void loadRedis() {
  479. List<SysDict> sysDicts = list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getStatus, true));
  480. List<SysDictRedis> sysDictRedis = Beans.copy(SysDictRedis.class, sysDicts);
  481. JList<SysDictRedis> list = new JArrayList<>(sysDictRedis);
  482. //父字典
  483. JList<SysDictRedis> paters = list.filter().isNull(SysDictRedis::getPaterCode).list();
  484. JList<SysDictRedis> paters2 = list.filter().eq(SysDictRedis::getPaterCode, "").list();
  485. if (Emptys.check(paters2)) {
  486. paters.addAll(paters2);
  487. }
  488. redisService.removeLikeMap(SysDictUtils.SYS_DICT_REDIS_PREFIX);
  489. //[父code -> 子列表]数据结构写入redis
  490. paters.forEach(sysDict -> {
  491. JList<SysDictRedis> sysDictJList = list.filter().eq(SysDictRedis::getPaterCode, sysDict.getCode()).list();
  492. sysDictJList.forEach(sysDictRediss -> redisService.setMap(SysDictUtils.getKey(sysDict.getCode()), sysDictRediss.getCode(), sysDictRediss));
  493. });
  494. log.info("字典写入redis完成~");
  495. }
  496. }