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完成~");
}
}