Compare commits
13 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
0cbd73f03b | |
|
|
73b42af29f | |
|
|
5dbe659c51 | |
|
|
a4f8a37a59 | |
|
|
f3f1061250 | |
|
|
db32b93ae7 | |
|
|
9603d42295 | |
|
|
24b5f3ea5d | |
|
|
a1dd6c7545 | |
|
|
92610d6cd9 | |
|
|
699fb21398 | |
|
|
99d9e46835 | |
|
|
9eb885c7b9 |
|
|
@ -5,6 +5,8 @@ import com.microservices.common.core.constant.ServiceNameConstants;
|
|||
import com.microservices.common.core.domain.R;
|
||||
import com.microservices.system.api.domain.SysDept;
|
||||
import com.microservices.system.api.factory.RemoteZoneFallbackFactory;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
|
@ -121,4 +123,31 @@ public interface RemoteZoneService {
|
|||
public R<Boolean> lockCmsByDeptId(@PathVariable(value = "deptId") Long deptId
|
||||
, @PathVariable(value = "lockStatus") Integer lockStatus
|
||||
, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
|
||||
/**
|
||||
* 根据多个字典条件查询文章主键列表(AND 逻辑)
|
||||
*
|
||||
* @param objectType 对象类型(如:CMS_DOC)
|
||||
* @param dictFiltersJson 字典筛选条件JSON数组
|
||||
* @param source 来源
|
||||
* @return 文章主键列表
|
||||
*/
|
||||
@PostMapping("/objectDict/objectIdsByFilters")
|
||||
R<List<Long>> getArticleIdsByDictFilters(@RequestParam("objectType") String objectType,
|
||||
@RequestBody JSONArray dictFiltersJson,
|
||||
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
|
||||
/**
|
||||
* 批量查询多个对象的字典属性(含标签解析)
|
||||
* 返回 List<JSONObject>,每条含 objectId/dictType/dictCode/dictLabel/dictValue
|
||||
*
|
||||
* @param objectType 对象类型(如 ARTICLE)
|
||||
* @param objectIds 对象主键数组
|
||||
* @param source 来源
|
||||
* @return 字典属性列表
|
||||
*/
|
||||
@PostMapping("/objectDict/dictsByObjectIds")
|
||||
R<List<JSONObject>> getDictsByObjectIds(@RequestParam("objectType") String objectType,
|
||||
@RequestBody Long[] objectIds,
|
||||
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.microservices.system.api.factory;
|
|||
import com.microservices.common.core.domain.R;
|
||||
import com.microservices.system.api.RemoteZoneService;
|
||||
import com.microservices.system.api.domain.SysDept;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||
|
|
@ -68,6 +70,16 @@ public class RemoteZoneFallbackFactory implements FallbackFactory<RemoteZoneServ
|
|||
public R<Boolean> lockCmsByDeptId(Long deptId, Integer lockStatus, String source) {
|
||||
return R.fail("通过组织id锁定/解锁专区资讯失败:" + throwable.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<List<Long>> getArticleIdsByDictFilters(String objectType, JSONArray dictFiltersJson, String source) {
|
||||
return R.fail("根据字典条件查询文章ID失败:" + throwable.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<List<JSONObject>> getDictsByObjectIds(String objectType, Long[] objectIds, String source) {
|
||||
return R.fail("批量查询对象字典属性失败:" + throwable.getMessage());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
package com.microservices.cms.checkin.controller;
|
||||
|
||||
import com.microservices.cms.checkin.domain.CmsDocCheckinInputDto;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinQueryVo;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinVo;
|
||||
import com.microservices.cms.checkin.service.ICmsDocCheckinService;
|
||||
import com.microservices.common.core.web.controller.BaseController;
|
||||
import com.microservices.common.core.web.domain.AjaxResult;
|
||||
import com.microservices.common.core.web.page.TableDataInfo;
|
||||
import com.microservices.common.log.annotation.Log;
|
||||
import com.microservices.common.log.enums.BusinessType;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章打卡Controller
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Api(tags = "文章打卡相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/docCheckin")
|
||||
public class CmsDocCheckinController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private ICmsDocCheckinService cmsDocCheckinService;
|
||||
|
||||
/**
|
||||
* 文章打卡(一人一文一次,重复打卡将被拦截)
|
||||
*/
|
||||
@ApiOperation("文章打卡")
|
||||
@Log(title = "文章打卡", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/{docId}")
|
||||
public AjaxResult checkin(@ApiParam(name = "docId", value = "文章Id", required = true) @PathVariable("docId") Long docId,
|
||||
@RequestBody(required = false) CmsDocCheckinInputDto dto) {
|
||||
return AjaxResult.success(cmsDocCheckinService.checkin(docId, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询本人打卡记录(分页)
|
||||
*/
|
||||
@ApiOperation("查询本人打卡记录")
|
||||
@GetMapping("/myList")
|
||||
public TableDataInfo myList(CmsDocCheckinQueryVo query) {
|
||||
startPage();
|
||||
List<CmsDocCheckinVo> list = cmsDocCheckinService.selectMyCheckinList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按文章ID列表批量查询当前用户的打卡记录(用于标记"我已打卡")
|
||||
*/
|
||||
@ApiOperation("按文章ID列表批量查询当前用户已打卡文章")
|
||||
@PostMapping("/byDocIds")
|
||||
public AjaxResult byDocIds(@RequestBody Long[] docIds) {
|
||||
return AjaxResult.success(cmsDocCheckinService.selectCheckinByDocIds(Arrays.asList(docIds)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.microservices.cms.checkin.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.microservices.common.core.web.domain.BaseEntity;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 文章打卡记录对象 cms_doc_checkin
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("文章打卡记录")
|
||||
public class CmsDocCheckin extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("主键")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("文章ID")
|
||||
private Long docId;
|
||||
|
||||
@ApiModelProperty("打卡用户ID")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty("照片文件标识(逗号分隔)")
|
||||
private String fileIdentifier;
|
||||
|
||||
@ApiModelProperty("打卡备注")
|
||||
private String notes;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("打卡时间(服务端记录)")
|
||||
private Date checkinTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.microservices.cms.checkin.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章打卡输入对象
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("文章打卡输入对象")
|
||||
public class CmsDocCheckinInputDto {
|
||||
|
||||
@ApiModelProperty("照片文件标识列表(选填)")
|
||||
private List<String> fileIdentifier;
|
||||
|
||||
@ApiModelProperty("打卡备注(选填)")
|
||||
private String notes;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.microservices.cms.checkin.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文章打卡记录查询条件(本人打卡记录分页查询)
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
public class CmsDocCheckinQueryVo {
|
||||
|
||||
@ApiModelProperty("文章标题(模糊)")
|
||||
private String docName;
|
||||
|
||||
@ApiModelProperty("打卡备注(模糊)")
|
||||
private String notes;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.microservices.cms.checkin.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.microservices.system.api.domain.SysFileInfo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章打卡展示对象
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("文章打卡展示对象")
|
||||
public class CmsDocCheckinVo {
|
||||
|
||||
@ApiModelProperty("打卡记录主键")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty("文章ID")
|
||||
private Long docId;
|
||||
|
||||
@ApiModelProperty("文章标题")
|
||||
private String docName;
|
||||
|
||||
@ApiModelProperty("照片文件标识(逗号分隔)")
|
||||
private String fileIdentifier;
|
||||
|
||||
@ApiModelProperty("文件信息列表(按 fileIdentifier 解析填充)")
|
||||
private List<SysFileInfo> fileList;
|
||||
|
||||
@ApiModelProperty("打卡备注")
|
||||
private String notes;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("打卡时间")
|
||||
private Date checkinTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("打卡记录创建时间")
|
||||
private Date createTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.microservices.cms.checkin.mapper;
|
||||
|
||||
import com.microservices.cms.checkin.domain.CmsDocCheckin;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinQueryVo;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章打卡记录Mapper接口
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
public interface CmsDocCheckinMapper {
|
||||
|
||||
/**
|
||||
* 唯一性检查:查询某用户对某文章的打卡记录
|
||||
*/
|
||||
CmsDocCheckin selectByDocIdAndUserId(@Param("docId") Long docId, @Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 新增打卡记录
|
||||
*/
|
||||
int insertCmsDocCheckin(CmsDocCheckin cmsDocCheckin);
|
||||
|
||||
/**
|
||||
* 查询本人打卡记录(JOIN cms_doc 取文章标题)
|
||||
*/
|
||||
List<CmsDocCheckinVo> selectMyList(@Param("userId") Long userId, @Param("query") CmsDocCheckinQueryVo query);
|
||||
|
||||
/**
|
||||
* 按文章ID列表批量查询当前用户的打卡记录
|
||||
*/
|
||||
List<CmsDocCheckinVo> selectByDocIdsAndUserId(@Param("docIds") List<Long> docIds, @Param("userId") Long userId);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.microservices.cms.checkin.service;
|
||||
|
||||
import com.microservices.cms.checkin.domain.CmsDocCheckinInputDto;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinQueryVo;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章打卡记录Service接口
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
public interface ICmsDocCheckinService {
|
||||
|
||||
/**
|
||||
* 文章打卡(一人一文一次,重复打卡抛异常)
|
||||
*
|
||||
* @param docId 文章ID
|
||||
* @param dto 打卡入参(照片文件标识列表、备注)
|
||||
* @return 打卡展示对象
|
||||
*/
|
||||
CmsDocCheckinVo checkin(Long docId, CmsDocCheckinInputDto dto);
|
||||
|
||||
/**
|
||||
* 查询本人打卡记录(JOIN 文章标题)
|
||||
*
|
||||
* @param query 查询条件(文章标题/备注模糊)
|
||||
* @return 打卡记录列表
|
||||
*/
|
||||
List<CmsDocCheckinVo> selectMyCheckinList(CmsDocCheckinQueryVo query);
|
||||
|
||||
/**
|
||||
* 按文章ID列表批量查询当前用户的打卡记录
|
||||
*
|
||||
* @param docIds 文章ID列表
|
||||
* @return 当前用户已打卡的文章及打卡详情
|
||||
*/
|
||||
List<CmsDocCheckinVo> selectCheckinByDocIds(List<Long> docIds);
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
package com.microservices.cms.checkin.service.impl;
|
||||
|
||||
import com.microservices.cms.checkin.domain.CmsDocCheckin;
|
||||
import com.microservices.cms.checkin.domain.CmsDocCheckinInputDto;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinQueryVo;
|
||||
import com.microservices.cms.checkin.domain.vo.CmsDocCheckinVo;
|
||||
import com.microservices.cms.checkin.mapper.CmsDocCheckinMapper;
|
||||
import com.microservices.cms.checkin.service.ICmsDocCheckinService;
|
||||
import com.microservices.cms.doc.service.ICmsDocService;
|
||||
import com.microservices.common.core.exception.ServiceException;
|
||||
import com.microservices.common.core.utils.DateUtils;
|
||||
import com.microservices.common.core.utils.StringUtils;
|
||||
import com.microservices.common.security.utils.SecurityUtils;
|
||||
import com.microservices.system.api.RemoteFileService;
|
||||
import com.microservices.system.api.domain.SysFileInfo;
|
||||
import com.microservices.system.api.utils.FeignUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 文章打卡记录Service业务层处理
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Service
|
||||
public class CmsDocCheckinServiceImpl implements ICmsDocCheckinService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CmsDocCheckinServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private CmsDocCheckinMapper cmsDocCheckinMapper;
|
||||
|
||||
@Autowired
|
||||
private ICmsDocService cmsDocService;
|
||||
|
||||
@Autowired
|
||||
private RemoteFileService remoteFileService;
|
||||
|
||||
@Override
|
||||
public CmsDocCheckinVo checkin(Long docId, CmsDocCheckinInputDto dto) {
|
||||
if (docId == null) {
|
||||
throw new ServiceException("文章ID不能为空");
|
||||
}
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
if (userId == null) {
|
||||
throw new ServiceException("未获取到登录用户信息,请重新登录");
|
||||
}
|
||||
|
||||
// ① 校验文章存在(不存在或为栏目即抛异常)
|
||||
cmsDocService.selectBaseCmsDocById(docId);
|
||||
|
||||
// ② 校验文件标识存在性(每个 fileIdentifier 必须对应真实文件)
|
||||
List<String> identifiers = (dto != null) ? dto.getFileIdentifier() : null;
|
||||
if (identifiers != null && !identifiers.isEmpty()) {
|
||||
checkFileIdentifiersExist(identifiers);
|
||||
}
|
||||
|
||||
// 唯一性检查:一人一文只能打卡一次
|
||||
CmsDocCheckin exist = cmsDocCheckinMapper.selectByDocIdAndUserId(docId, userId);
|
||||
if (exist != null) {
|
||||
throw new ServiceException("您已打卡过该文章");
|
||||
}
|
||||
|
||||
// 组装打卡记录(fileIdentifier 由前端传入的文件标识列表以逗号拼接)
|
||||
CmsDocCheckin checkin = new CmsDocCheckin();
|
||||
checkin.setDocId(docId);
|
||||
checkin.setUserId(userId);
|
||||
if (identifiers != null && !identifiers.isEmpty()) {
|
||||
checkin.setFileIdentifier(String.join(",", identifiers));
|
||||
}
|
||||
if (dto != null) {
|
||||
checkin.setNotes(dto.getNotes());
|
||||
}
|
||||
checkin.setCheckinTime(DateUtils.getNowDate());
|
||||
checkin.setCreateAndUpdate(SecurityUtils.getUsername());
|
||||
|
||||
cmsDocCheckinMapper.insertCmsDocCheckin(checkin);
|
||||
|
||||
// 返回打卡结果(不含文章标题,前端已有文章上下文)
|
||||
CmsDocCheckinVo vo = new CmsDocCheckinVo();
|
||||
vo.setId(checkin.getId());
|
||||
vo.setDocId(checkin.getDocId());
|
||||
vo.setFileIdentifier(checkin.getFileIdentifier());
|
||||
vo.setNotes(checkin.getNotes());
|
||||
vo.setCheckinTime(checkin.getCheckinTime());
|
||||
vo.setCreateTime(checkin.getCreateTime());
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CmsDocCheckinVo> selectMyCheckinList(CmsDocCheckinQueryVo query) {
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
if (userId == null) {
|
||||
throw new ServiceException("未获取到登录用户信息,请重新登录");
|
||||
}
|
||||
List<CmsDocCheckinVo> list = cmsDocCheckinMapper.selectMyList(userId, query);
|
||||
fillFileInfo(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CmsDocCheckinVo> selectCheckinByDocIds(List<Long> docIds) {
|
||||
if (docIds == null || docIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
if (userId == null) {
|
||||
throw new ServiceException("未获取到登录用户信息,请重新登录");
|
||||
}
|
||||
List<CmsDocCheckinVo> list = cmsDocCheckinMapper.selectByDocIdsAndUserId(docIds, userId);
|
||||
fillFileInfo(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验文件标识列表是否都对应真实文件
|
||||
* (getFileListByIdentifier 对不存在的标识会静默跳过,故需显式比对覆盖)
|
||||
*/
|
||||
private void checkFileIdentifiersExist(List<String> identifiers) {
|
||||
List<SysFileInfo> files = FeignUtils.getReturnData(
|
||||
remoteFileService.getFileListByIdentifier(String.join(",", identifiers)));
|
||||
Set<String> found = (files == null) ? Collections.emptySet()
|
||||
: files.stream().map(SysFileInfo::getFileIdentifier).collect(Collectors.toSet());
|
||||
for (String fid : identifiers) {
|
||||
if (!found.contains(fid)) {
|
||||
throw new ServiceException("文件不存在(文件标识[%s])", fid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量填充打卡记录的文件信息(一次 Feign 查询,避免 N+1)
|
||||
* 文件信息为增强展示,文件服务异常时降级为不填充,不阻断主查询
|
||||
*/
|
||||
private void fillFileInfo(List<CmsDocCheckinVo> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 收集所有 fileIdentifier(逗号分隔)去重合并
|
||||
Set<String> allIds = list.stream()
|
||||
.map(CmsDocCheckinVo::getFileIdentifier)
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.flatMap(s -> Arrays.stream(s.split(",")))
|
||||
.collect(Collectors.toSet());
|
||||
if (allIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
List<SysFileInfo> files = FeignUtils.getReturnData(
|
||||
remoteFileService.getFileListByIdentifier(String.join(",", allIds)));
|
||||
if (files == null || files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, SysFileInfo> map = files.stream()
|
||||
.collect(Collectors.toMap(SysFileInfo::getFileIdentifier, f -> f, (a, b) -> a));
|
||||
for (CmsDocCheckinVo vo : list) {
|
||||
if (StringUtils.isEmpty(vo.getFileIdentifier())) {
|
||||
continue;
|
||||
}
|
||||
List<SysFileInfo> voFiles = Arrays.stream(vo.getFileIdentifier().split(","))
|
||||
.map(map::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
vo.setFileList(voFiles);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("填充打卡文件信息失败,降级为不填充", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,8 +149,9 @@ public class CmsDocController extends BaseController {
|
|||
@RequiresPermissions("cms:doc:query")
|
||||
@ApiOperation("获取文章详细信息")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@ApiParam(name = "id", value = "文章Id") @PathVariable("id") Long id) {
|
||||
return success(cmsDocService.selectCmsDocById(id, false, null));
|
||||
public AjaxResult getInfo(@ApiParam(name = "id", value = "文章Id") @PathVariable("id") Long id,
|
||||
@RequestParam(value = "withDict", required = false, defaultValue = "false") Boolean withDict) {
|
||||
return success(cmsDocService.selectCmsDocById(id, false, null, withDict));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -179,8 +179,10 @@ public class OpenController extends BaseController {
|
|||
*/
|
||||
@ApiOperation("获取文章详细信息")
|
||||
@GetMapping(value = "/{id}")
|
||||
public GenericsAjaxResult<CmsDocDetailDto> getInfo(@ApiParam(name = "id", value = "文章Id") @PathVariable("id") Long id, HttpServletRequest request) {
|
||||
return genericsSuccess(cmsDocService.selectCmsDocById(id, true, request));
|
||||
public GenericsAjaxResult<CmsDocDetailDto> getInfo(@ApiParam(name = "id", value = "文章Id") @PathVariable("id") Long id,
|
||||
@RequestParam(value = "withDict", required = false, defaultValue = "false") Boolean withDict,
|
||||
HttpServletRequest request) {
|
||||
return genericsSuccess(cmsDocService.selectCmsDocById(id, true, request, withDict));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package com.microservices.cms.doc.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.microservices.cms.doc.domain.vo.DictAttributeVo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author otto
|
||||
|
|
@ -106,6 +109,13 @@ public class CmsDocBaseDto {
|
|||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 字典扩展属性列表(withDict=true 时返回)
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@ApiModelProperty("字典扩展属性列表(withDict=true 时返回)")
|
||||
private List<DictAttributeVo> dictAttributes;
|
||||
|
||||
public void setPublishTime(Date publishTime) {
|
||||
if (publishTime != null) {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.microservices.cms.doc.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.microservices.cms.doc.domain.vo.DictFilterVo;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author otto
|
||||
|
|
@ -43,6 +45,18 @@ public class CmsDocSearchVo {
|
|||
@ApiModelProperty(value = "发布时间",hidden = true)
|
||||
private Date publishTime;
|
||||
|
||||
/**
|
||||
* 发布年份(如:2026)
|
||||
*/
|
||||
@ApiModelProperty(value = "发布年份(如:2026)")
|
||||
private Integer publishYear;
|
||||
|
||||
/**
|
||||
* 发布月份(0或空代表查询全年,1-12查询对应月份)
|
||||
*/
|
||||
@ApiModelProperty(value = "发布月份(0或空代表查询全年,1-12查询对应月份)")
|
||||
private Integer publishMonth;
|
||||
|
||||
/**
|
||||
* 是否为栏目
|
||||
*/
|
||||
|
|
@ -89,4 +103,16 @@ public class CmsDocSearchVo {
|
|||
*/
|
||||
@ApiModelProperty(value = "排除文章Id", hidden = true)
|
||||
private Long excludeId;
|
||||
|
||||
/**
|
||||
* 字典筛选条件列表(transient,不参与数据库查询)
|
||||
*/
|
||||
@ApiModelProperty(value = "字典筛选条件列表")
|
||||
private List<DictFilterVo> dictFilters;
|
||||
|
||||
/**
|
||||
* 是否按需加载字典扩展属性(默认 false,不加载)
|
||||
*/
|
||||
@ApiModelProperty(value = "是否加载字典扩展属性")
|
||||
private Boolean withDict;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.microservices.cms.doc.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文章字典属性(按需加载,扁平结构)
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("文章字典属性")
|
||||
public class DictAttributeVo {
|
||||
|
||||
@ApiModelProperty("字典类型Key")
|
||||
private String dictType;
|
||||
|
||||
@ApiModelProperty("字典类型名称")
|
||||
private String dictTypeName;
|
||||
|
||||
@ApiModelProperty("字典数据标签")
|
||||
private String dictLabel;
|
||||
|
||||
@ApiModelProperty("字典数据值")
|
||||
private String dictValue;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.microservices.cms.doc.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 字典筛选条件VO
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
public class DictFilterVo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "字典类型")
|
||||
private String dictType;
|
||||
|
||||
@ApiModelProperty(value = "字典键值")
|
||||
private String dictValue;
|
||||
}
|
||||
|
|
@ -40,6 +40,16 @@ public interface CmsDocMapper {
|
|||
*/
|
||||
public List<CmsDoc> selectCmsDocList(CmsDocSearchVo cmsDocSearchVo);
|
||||
|
||||
/**
|
||||
* 查询文章信息列表(支持字典筛选)
|
||||
*
|
||||
* @param cmsDocSearchVo 文章信息
|
||||
* @param articleIds 字典筛选后的文章ID列表
|
||||
* @return 文章信息集合
|
||||
*/
|
||||
public List<CmsDoc> selectCmsDocList(@Param("searchVo") CmsDocSearchVo cmsDocSearchVo,
|
||||
@Param("articleIds") List<Long> articleIds);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -96,7 +106,7 @@ public interface CmsDocMapper {
|
|||
*/
|
||||
CmsDoc selectCmsDocByNameAndDeptIdIgnoreDirName(@Param("name") String name, @Param("deptId") Long deptId);
|
||||
|
||||
List<CmsDoc> selectHotCmsDocList(CmsDoc cmsDocInput);
|
||||
List<CmsDoc> selectHotCmsDocList(CmsDocSearchVo cmsDocSearchVo);
|
||||
|
||||
List<CmsDoc> selectCmsDocDirByDeptIdAndInName(@Param("deptId") Long deptId, @Param("cmsDirNameList") List<String> cmsDirNameList);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
|
|||
import com.microservices.cms.doc.domain.*;
|
||||
import com.microservices.cms.doc.domain.gitlink.EntryDto;
|
||||
import com.microservices.cms.doc.domain.gitlink.FileEntryDto;
|
||||
import com.microservices.cms.doc.domain.vo.DictFilterVo;
|
||||
import com.microservices.cms.project.domain.CmsProject;
|
||||
import com.microservices.common.core.web.page.GenericsTableDataInfo;
|
||||
|
||||
|
|
@ -26,6 +27,17 @@ public interface ICmsDocService {
|
|||
*/
|
||||
public CmsDocDetailDto selectCmsDocById(Long id, boolean isOpen, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 查询文章信息(可按需加载字典扩展属性)
|
||||
*
|
||||
* @param id 文章信息主键
|
||||
* @param isOpen 是否公开接口
|
||||
* @param request 请求对象
|
||||
* @param withDict 是否按需加载字典扩展属性
|
||||
* @return 文章信息
|
||||
*/
|
||||
public CmsDocDetailDto selectCmsDocById(Long id, boolean isOpen, HttpServletRequest request, boolean withDict);
|
||||
|
||||
/**
|
||||
* 查询文章基础信息
|
||||
*
|
||||
|
|
@ -217,6 +229,14 @@ public interface ICmsDocService {
|
|||
* @param cmsDocSearchVo
|
||||
* @return
|
||||
*/
|
||||
|
||||
/**
|
||||
* 根据多个字典条件查询文章主键列表
|
||||
*
|
||||
* @param dictFilters 字典筛选条件
|
||||
* @return 文章主键列表
|
||||
*/
|
||||
List<Long> selectArticleIdsByDictFilters(List<DictFilterVo> dictFilters);
|
||||
int selectCmsDocCountByDept(Long deptId, CmsDocSearchVo cmsDocSearchVo);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
package com.microservices.cms.doc.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.microservices.cms.doc.domain.*;
|
||||
import com.microservices.cms.doc.domain.gitlink.EntryDto;
|
||||
import com.microservices.cms.doc.domain.gitlink.FileEntryDto;
|
||||
import com.microservices.cms.doc.domain.vo.DictAttributeVo;
|
||||
import com.microservices.cms.doc.domain.vo.DictFilterVo;
|
||||
import com.microservices.cms.doc.mapper.CmsDocMapper;
|
||||
import com.microservices.cms.doc.service.ICmsAsyncService;
|
||||
import com.microservices.cms.doc.service.ICmsDocCommentService;
|
||||
|
|
@ -16,6 +19,7 @@ import com.microservices.common.core.constant.CacheConstants;
|
|||
import com.microservices.common.core.constant.HttpStatus;
|
||||
import com.microservices.common.core.constant.SecurityConstants;
|
||||
import com.microservices.common.core.constant.UserConstants;
|
||||
import com.microservices.common.core.domain.R;
|
||||
import com.microservices.common.core.enums.DocAndZoneOperation;
|
||||
import com.microservices.common.core.enums.DocAuditStatus;
|
||||
import com.microservices.common.core.enums.SystemRole;
|
||||
|
|
@ -102,6 +106,11 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
*/
|
||||
@Override
|
||||
public CmsDocDetailDto selectCmsDocById(Long id, boolean isOpen, HttpServletRequest request) {
|
||||
return selectCmsDocById(id, isOpen, request, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CmsDocDetailDto selectCmsDocById(Long id, boolean isOpen, HttpServletRequest request, boolean withDict) {
|
||||
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
|
||||
if (cmsDoc == null || !canVisitNotPassedDoc(cmsDoc, isOpen, request)) {
|
||||
throw new ServiceException(HttpStatus.NOT_FOUND, "该文章不存在(文章Id[" + id + "])");
|
||||
|
|
@ -133,9 +142,61 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
if (isOpen && DocAuditStatus.AUDIT_PASS.getAuditCode().equals(cmsDoc.getAuditStatus())) {
|
||||
updateVisits(cmsDocDto.getId());
|
||||
}
|
||||
if (withDict) {
|
||||
fillDictAttributes(Collections.singletonList(cmsDocDto));
|
||||
}
|
||||
return cmsDocDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按需批量填充字典扩展属性。
|
||||
* 收集所有文章 ID → 一次 Feign 取字典 → 按 objectId 分组 → 填充到每个 DTO。
|
||||
* Feign 降级或异常时返回空列表,不影响主查询。
|
||||
*/
|
||||
private void fillDictAttributes(List<? extends CmsDocBaseDto> dtoList) {
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<Long> articleIds = dtoList.stream()
|
||||
.map(CmsDocBaseDto::getId)
|
||||
.filter(id -> id != null)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (articleIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
R<List<JSONObject>> result = remoteZoneService.getDictsByObjectIds(
|
||||
"CMS_DOC", articleIds.toArray(new Long[0]), SecurityConstants.INNER);
|
||||
if (result == null || !R.isSuccess(result) || result.getData() == null) {
|
||||
logger.warn("按需加载文章字典属性降级返回空(原因: {})",
|
||||
result != null ? result.getMsg() : "null");
|
||||
dtoList.forEach(dto -> dto.setDictAttributes(Collections.emptyList()));
|
||||
return;
|
||||
}
|
||||
Map<Long, List<JSONObject>> dictsByObjectId = result.getData().stream()
|
||||
.collect(Collectors.groupingBy(j -> j.getLong("objectId")));
|
||||
for (CmsDocBaseDto dto : dtoList) {
|
||||
List<JSONObject> dicts = dictsByObjectId.get(dto.getId());
|
||||
if (dicts == null || dicts.isEmpty()) {
|
||||
dto.setDictAttributes(Collections.emptyList());
|
||||
} else {
|
||||
dto.setDictAttributes(dicts.stream().map(j -> {
|
||||
DictAttributeVo vo = new DictAttributeVo();
|
||||
vo.setDictType(j.getString("dictType"));
|
||||
vo.setDictTypeName(j.getString("dictTypeName"));
|
||||
vo.setDictLabel(j.getString("dictLabel"));
|
||||
vo.setDictValue(j.getString("dictValue"));
|
||||
return vo;
|
||||
}).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("按需加载文章字典属性异常,降级返回空列表", e);
|
||||
dtoList.forEach(dto -> dto.setDictAttributes(Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CmsDoc selectBaseCmsDocById(Long id) {
|
||||
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
|
||||
|
|
@ -182,8 +243,24 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
public List<CmsDocAllDto> selectCmsDocListByDept(Long deptId, CmsDocSearchVo cmsDocSearchVo) {
|
||||
cmsDocSearchVo.setDeptId(deptId);
|
||||
cmsDocSearchVo.setIsDir(false);
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearchVo);
|
||||
return cmsDocList.stream().map(CmsDoc::copyToCmsDocAllDto).collect(Collectors.toList());
|
||||
|
||||
// 如果有字典筛选条件,先获取符合条件的文章 ID
|
||||
List<Long> articleIds = null;
|
||||
if (cmsDocSearchVo.getDictFilters() != null && !cmsDocSearchVo.getDictFilters().isEmpty()) {
|
||||
articleIds = selectArticleIdsByDictFilters(cmsDocSearchVo.getDictFilters());
|
||||
|
||||
// 如果没有符合条件的文章,直接返回空列表
|
||||
if (articleIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocList(cmsDocSearchVo, articleIds);
|
||||
List<CmsDocAllDto> dtoList = cmsDocList.stream().map(CmsDoc::copyToCmsDocAllDto).collect(Collectors.toList());
|
||||
if (Boolean.TRUE.equals(cmsDocSearchVo.getWithDict())) {
|
||||
fillDictAttributes(dtoList);
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -339,7 +416,11 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
if (cmsDocList == null || cmsDocList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return cmsDocList.stream().map(CmsDoc::copyToCmsDocBaseDto).collect(Collectors.toList());
|
||||
List<CmsDocBaseDto> dtoList = cmsDocList.stream().map(CmsDoc::copyToCmsDocBaseDto).collect(Collectors.toList());
|
||||
if (Boolean.TRUE.equals(cmsDocSearchVo.getWithDict())) {
|
||||
fillDictAttributes(dtoList);
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -354,11 +435,46 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
|
||||
cmsDocSearchVo.setDeptId(cmsDocDir.getDeptId());
|
||||
cmsDocSearchVo.setDirName(cmsDocDir.getName());
|
||||
|
||||
// 参数验证:发布月份(0或空代表查询全年,1-12查询对应月份)
|
||||
Integer publishMonth = cmsDocSearchVo.getPublishMonth();
|
||||
if (publishMonth != null && (publishMonth < 0 || publishMonth > 12)) {
|
||||
throw new ServiceException("月份必须在0-12之间(0或空代表全年)");
|
||||
}
|
||||
|
||||
// 处理字典筛选条件:如果存在字典筛选条件,先获取符合条件的文章ID列表
|
||||
List<Long> dictFilteredIds = null;
|
||||
if (cmsDocSearchVo.getDictFilters() != null && !cmsDocSearchVo.getDictFilters().isEmpty()) {
|
||||
dictFilteredIds = selectArticleIdsByDictFilters(cmsDocSearchVo.getDictFilters());
|
||||
// 如果没有符合条件的文章,直接返回空列表
|
||||
if (dictFilteredIds == null || dictFilteredIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectCmsDocListSetDefaultHeadImg(cmsDocSearchVo);
|
||||
if (cmsDocList == null || cmsDocList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return cmsDocList.stream().map(CmsDoc::copyToCmsDocBaseDto).collect(Collectors.toList());
|
||||
|
||||
// 如果有字典筛选条件,在内存中过滤结果(只保留符合字典条件的文章)
|
||||
List<CmsDoc> filteredList = cmsDocList;
|
||||
if (dictFilteredIds != null && !dictFilteredIds.isEmpty()) {
|
||||
Set<Long> dictFilteredIdSet = new HashSet<>(dictFilteredIds);
|
||||
filteredList = cmsDocList.stream()
|
||||
.filter(doc -> dictFilteredIdSet.contains(doc.getId()))
|
||||
.collect(Collectors.toList());
|
||||
// 如果过滤后为空,直接返回空列表
|
||||
if (filteredList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
List<CmsDocBaseDto> dtoList = filteredList.stream().map(CmsDoc::copyToCmsDocBaseDto).collect(Collectors.toList());
|
||||
if (Boolean.TRUE.equals(cmsDocSearchVo.getWithDict())) {
|
||||
fillDictAttributes(dtoList);
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1142,12 +1258,12 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
|
||||
@Override
|
||||
public List<HotCmsDocDto> selectHotCmsDocList(Long dirId) {
|
||||
CmsDoc cmsDocInput = new CmsDoc();
|
||||
CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
|
||||
CmsDoc cmsDir = selectCmsDir(dirId);
|
||||
cmsDocInput.setDeptId(cmsDir.getDeptId());
|
||||
cmsDocInput.setIsDir(false);
|
||||
cmsDocInput.setDirName(cmsDir.getName());
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectHotCmsDocList(cmsDocInput);
|
||||
cmsDocSearchVo.setDeptId(cmsDir.getDeptId());
|
||||
cmsDocSearchVo.setIsDir(false);
|
||||
cmsDocSearchVo.setDirName(cmsDir.getName());
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectHotCmsDocList(cmsDocSearchVo);
|
||||
if (cmsDocList != null) {
|
||||
//todo:需检查该文章是否在GitLink中存在
|
||||
return cmsDocList.stream().map(CmsDoc::copyToHotCmsDocDto).collect(Collectors.toList());
|
||||
|
|
@ -1178,10 +1294,10 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
|
||||
@Override
|
||||
public List<HotCmsDocDto> selectHotCmsDocListByDept(Long deptId) {
|
||||
CmsDoc cmsDocInput = new CmsDoc();
|
||||
cmsDocInput.setDeptId(deptId);
|
||||
cmsDocInput.setIsDir(false);
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectHotCmsDocList(cmsDocInput);
|
||||
CmsDocSearchVo cmsDocSearchVo = new CmsDocSearchVo();
|
||||
cmsDocSearchVo.setDeptId(deptId);
|
||||
cmsDocSearchVo.setIsDir(false);
|
||||
List<CmsDoc> cmsDocList = cmsDocMapper.selectHotCmsDocList(cmsDocSearchVo);
|
||||
if (cmsDocList != null) {
|
||||
//todo:需检查该文章是否在GitLink中存在
|
||||
return cmsDocList.stream().map(CmsDoc::copyToHotCmsDocDto).collect(Collectors.toList());
|
||||
|
|
@ -1597,4 +1713,26 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectArticleIdsByDictFilters(List<DictFilterVo> dictFilters) {
|
||||
if (dictFilters == null || dictFilters.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用 Zone 服务获取符合条件的文章 ID
|
||||
R<List<Long>> result = remoteZoneService.getArticleIdsByDictFilters(
|
||||
"CMS_DOC", JSONArray.from(dictFilters), SecurityConstants.INNER);
|
||||
|
||||
if (result == null || !R.isSuccess(result)) {
|
||||
throw new ServiceException("获取字典筛选文章ID失败:" + (result != null ? result.getMsg() : "未知错误"));
|
||||
}
|
||||
|
||||
return result.getData() != null ? result.getData() : Collections.emptyList();
|
||||
} catch (Exception e) {
|
||||
logger.error("调用Zone服务获取字典筛选文章ID失败", e);
|
||||
throw new ServiceException("字典筛选文章查询失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.microservices.cms.checkin.mapper.CmsDocCheckinMapper">
|
||||
|
||||
<resultMap type="com.microservices.cms.checkin.domain.CmsDocCheckin" id="CmsDocCheckinResult">
|
||||
<id property="id" column="id"/>
|
||||
<result property="docId" column="doc_id"/>
|
||||
<result property="userId" column="user_id"/>
|
||||
<result property="fileIdentifier" column="file_identifier"/>
|
||||
<result property="notes" column="notes"/>
|
||||
<result property="checkinTime" column="checkin_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="com.microservices.cms.checkin.domain.vo.CmsDocCheckinVo" id="CmsDocCheckinVoResult">
|
||||
<id property="id" column="id"/>
|
||||
<result property="docId" column="doc_id"/>
|
||||
<result property="docName" column="doc_name"/>
|
||||
<result property="fileIdentifier" column="file_identifier"/>
|
||||
<result property="notes" column="notes"/>
|
||||
<result property="checkinTime" column="checkin_time"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCmsDocCheckinVo">
|
||||
select id, doc_id, user_id, file_identifier, notes, checkin_time,
|
||||
create_by, create_time, update_by, update_time
|
||||
from cms_doc_checkin
|
||||
</sql>
|
||||
|
||||
<select id="selectByDocIdAndUserId" resultMap="CmsDocCheckinResult">
|
||||
<include refid="selectCmsDocCheckinVo"/>
|
||||
where doc_id = #{docId} and user_id = #{userId}
|
||||
</select>
|
||||
|
||||
<insert id="insertCmsDocCheckin"
|
||||
parameterType="com.microservices.cms.checkin.domain.CmsDocCheckin"
|
||||
useGeneratedKeys="true"
|
||||
keyProperty="id">
|
||||
insert into cms_doc_checkin
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="docId != null">doc_id,</if>
|
||||
<if test="userId != null">user_id,</if>
|
||||
<if test="fileIdentifier != null and fileIdentifier != ''">file_identifier,</if>
|
||||
<if test="notes != null and notes != ''">notes,</if>
|
||||
<if test="checkinTime != null">checkin_time,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="docId != null">#{docId},</if>
|
||||
<if test="userId != null">#{userId},</if>
|
||||
<if test="fileIdentifier != null and fileIdentifier != ''">#{fileIdentifier},</if>
|
||||
<if test="notes != null and notes != ''">#{notes},</if>
|
||||
<if test="checkinTime != null">#{checkinTime},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<select id="selectMyList" resultMap="CmsDocCheckinVoResult">
|
||||
select c.id, c.doc_id, d.name as doc_name, c.file_identifier, c.notes, c.checkin_time, c.create_time
|
||||
from cms_doc_checkin c
|
||||
left join cms_doc d on c.doc_id = d.id
|
||||
where c.user_id = #{userId}
|
||||
<if test="query != null and query.docName != null and query.docName != ''">
|
||||
and d.name like concat('%', #{query.docName}, '%')
|
||||
</if>
|
||||
<if test="query != null and query.notes != null and query.notes != ''">
|
||||
and c.notes like concat('%', #{query.notes}, '%')
|
||||
</if>
|
||||
order by c.checkin_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectByDocIdsAndUserId" resultMap="CmsDocCheckinVoResult">
|
||||
select c.id, c.doc_id, d.name as doc_name, c.file_identifier, c.notes, c.checkin_time, c.create_time
|
||||
from cms_doc_checkin c
|
||||
left join cms_doc d on c.doc_id = d.id
|
||||
where c.user_id = #{userId}
|
||||
and c.doc_id in
|
||||
<foreach collection="docIds" item="docId" open="(" separator="," close=")">
|
||||
#{docId}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
where dept_id=#{deptId}
|
||||
</select>
|
||||
|
||||
<select id="selectCmsDocList" parameterType="com.microservices.cms.doc.domain.CmsDocSearchVo"
|
||||
<select id="selectCmsDocListSimple" parameterType="com.microservices.cms.doc.domain.CmsDocSearchVo"
|
||||
resultMap="CmsDocResult">
|
||||
<include refid="selectCmsDocVo"/>
|
||||
<include refid="selectListWhere"/>
|
||||
|
|
@ -212,6 +212,35 @@
|
|||
create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectCmsDocList" resultMap="CmsDocResult">
|
||||
<include refid="selectCmsDocVo"/>
|
||||
<where>
|
||||
<if test="searchVo.name != null and searchVo.name != ''">and name like concat('%', #{searchVo.name}, '%')</if>
|
||||
<if test="searchVo.keywords != null and searchVo.keywords != ''">and keywords like concat('%', #{searchVo.keywords}, '%')</if>
|
||||
<if test="searchVo.fileName != null and searchVo.fileName != ''">and file_name like concat('%', #{searchVo.fileName}, '%')</if>
|
||||
<if test="searchVo.filePath != null and searchVo.filePath != ''">and file_path = #{searchVo.filePath}</if>
|
||||
<if test="searchVo.publishTime != null ">and publish_time = #{searchVo.publishTime}</if>
|
||||
<if test="searchVo.isDir != null ">and is_dir = #{searchVo.isDir}</if>
|
||||
<if test="searchVo.dirName != null ">and dir_name = #{searchVo.dirName}</if>
|
||||
<if test="searchVo.deptId != null ">and dept_id = #{searchVo.deptId}</if>
|
||||
<if test="searchVo.isHomepage != null ">and is_homepage = #{searchVo.isHomepage}</if>
|
||||
<if test="searchVo.auditStatus != null and searchVo.auditStatus != '-1' ">and audit_status = #{searchVo.auditStatus}</if>
|
||||
<if test="searchVo.auditStatus == null ">and audit_status = '1'</if>
|
||||
<if test="searchVo.createBy != null and searchVo.createBy != '' ">and create_by = #{searchVo.createBy}</if>
|
||||
<if test="searchVo.excludeId != null ">and id != #{searchVo.excludeId}</if>
|
||||
<if test="articleIds != null and articleIds.size() > 0">
|
||||
AND id IN
|
||||
<foreach collection="articleIds" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY
|
||||
sort DESC,
|
||||
update_time DESC,
|
||||
create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectCmsDocById" parameterType="Long" resultMap="CmsDocResult">
|
||||
<include refid="selectCmsDocVo"/>
|
||||
where id = #{id}
|
||||
|
|
@ -257,7 +286,7 @@
|
|||
<include refid="selectCmsDocVo"/>
|
||||
where file_path = #{filePath} and dept_id = #{deptId} and is_dir = 0
|
||||
</select>
|
||||
<select id="selectHotCmsDocList" parameterType="com.microservices.cms.doc.domain.CmsDoc" resultMap="CmsDocResult">
|
||||
<select id="selectHotCmsDocList" parameterType="com.microservices.cms.doc.domain.CmsDocSearchVo" resultMap="CmsDocResult">
|
||||
<include refid="selectCmsDocAliasSetDefaultHeadImgVo"/>
|
||||
<include refid="selectListWhereAlias"/>
|
||||
ORDER BY
|
||||
|
|
@ -278,6 +307,18 @@
|
|||
<if test="fileName != null and fileName != ''">and file_name like concat('%', #{fileName}, '%')</if>
|
||||
<if test="filePath != null and filePath != ''">and file_path = #{filePath}</if>
|
||||
<if test="publishTime != null ">and publish_time = #{publishTime}</if>
|
||||
<if test="publishYear != null">
|
||||
<choose>
|
||||
<when test="publishMonth != null and publishMonth > 0">
|
||||
and publish_time >= STR_TO_DATE(CONCAT(#{publishYear}, '-', LPAD(#{publishMonth}, 2, '0'), '-01'), '%Y-%m-%d')
|
||||
and publish_time < DATE_ADD(STR_TO_DATE(CONCAT(#{publishYear}, '-', LPAD(#{publishMonth}, 2, '0'), '-01'), '%Y-%m-%d'), INTERVAL 1 MONTH)
|
||||
</when>
|
||||
<otherwise>
|
||||
and publish_time >= STR_TO_DATE(CONCAT(#{publishYear}, '-01-01'), '%Y-%m-%d')
|
||||
and publish_time < DATE_ADD(STR_TO_DATE(CONCAT(#{publishYear}, '-01-01'), '%Y-%m-%d'), INTERVAL 1 YEAR)
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
<if test="isDir != null ">and is_dir = #{isDir}</if>
|
||||
<if test="dirName != null ">and dir_name = #{dirName}</if>
|
||||
<if test="deptId != null ">and dept_id = #{deptId}</if>
|
||||
|
|
@ -296,6 +337,18 @@
|
|||
<if test="fileName != null and fileName != ''">and cd.file_name like concat('%', #{fileName}, '%')</if>
|
||||
<if test="filePath != null and filePath != ''">and cd.file_path = #{filePath}</if>
|
||||
<if test="publishTime != null ">and cd.publish_time = #{publishTime}</if>
|
||||
<if test="publishYear != null">
|
||||
<choose>
|
||||
<when test="publishMonth != null and publishMonth > 0">
|
||||
and cd.publish_time >= STR_TO_DATE(CONCAT(#{publishYear}, '-', LPAD(#{publishMonth}, 2, '0'), '-01'), '%Y-%m-%d')
|
||||
and cd.publish_time < DATE_ADD(STR_TO_DATE(CONCAT(#{publishYear}, '-', LPAD(#{publishMonth}, 2, '0'), '-01'), '%Y-%m-%d'), INTERVAL 1 MONTH)
|
||||
</when>
|
||||
<otherwise>
|
||||
and cd.publish_time >= STR_TO_DATE(CONCAT(#{publishYear}, '-01-01'), '%Y-%m-%d')
|
||||
and cd.publish_time < DATE_ADD(STR_TO_DATE(CONCAT(#{publishYear}, '-01-01'), '%Y-%m-%d'), INTERVAL 1 YEAR)
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
<if test="isDir != null ">and cd.is_dir = #{isDir}</if>
|
||||
<if test="dirName != null ">and cd.dir_name = #{dirName}</if>
|
||||
<if test="deptId != null ">and cd.dept_id = #{deptId}</if>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
-- 文章打卡记录表
|
||||
-- 说明:CMS 模块无 DDL 版本管理(flyway),此脚本需手动在目标库执行。
|
||||
-- 唯一索引 uk_doc_user 保证一人对一篇文章只能打卡一次(与 Service 层先查后插双保险)。
|
||||
CREATE TABLE cms_doc_checkin (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
doc_id BIGINT NOT NULL COMMENT '文章ID',
|
||||
user_id BIGINT NOT NULL COMMENT '打卡用户ID',
|
||||
file_identifier VARCHAR(500) COMMENT '照片文件标识(逗号分隔)',
|
||||
notes VARCHAR(500) COMMENT '打卡备注',
|
||||
checkin_time DATETIME NOT NULL COMMENT '打卡时间(服务端记录)',
|
||||
create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time DATETIME COMMENT '创建时间',
|
||||
update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time DATETIME COMMENT '更新时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_doc_user (doc_id, user_id) COMMENT '一人一文一次打卡唯一约束',
|
||||
KEY idx_user_id (user_id),
|
||||
KEY idx_doc_id (doc_id)
|
||||
) COMMENT='文章打卡记录';
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package com.microservices.zone.extension.controller;
|
||||
|
||||
import com.microservices.common.core.web.controller.BaseController;
|
||||
import com.microservices.common.core.web.domain.AjaxResult;
|
||||
import com.microservices.common.core.web.domain.GenericsAjaxResult;
|
||||
import com.microservices.common.core.web.page.GenericsTableDataInfo;
|
||||
import com.microservices.common.log.annotation.Log;
|
||||
import com.microservices.common.log.enums.BusinessType;
|
||||
import com.microservices.common.security.annotation.RequiresPermissions;
|
||||
import com.microservices.zone.extension.domain.ZoneObjectDict;
|
||||
import com.microservices.zone.extension.domain.vo.DictFilterVo;
|
||||
import com.microservices.zone.extension.domain.vo.ZoneObjectDictInputVo;
|
||||
import com.microservices.zone.extension.domain.vo.ZoneObjectDictSearchVo;
|
||||
import com.microservices.zone.extension.service.IZoneObjectDictService;
|
||||
import com.microservices.zone.specialProject.domain.vo.CommonTypeVo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 特色专区对象-字典扩展Controller
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/objectDict")
|
||||
@Api(tags = "特色专区对象-字典扩展接口")
|
||||
public class ZoneObjectDictController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IZoneObjectDictService zoneObjectDictService;
|
||||
|
||||
/**
|
||||
* 查询对象-字典绑定列表
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:list")
|
||||
@GetMapping("/list")
|
||||
@ApiOperation("查询对象-字典绑定列表")
|
||||
public GenericsTableDataInfo<ZoneObjectDict> list(@Validated ZoneObjectDictSearchVo searchVo) {
|
||||
startPage();
|
||||
List<ZoneObjectDict> list = zoneObjectDictService.selectZoneObjectDictList(searchVo.toZoneObjectDict());
|
||||
return getGenericsDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象-字典绑定详细信息
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:query")
|
||||
@GetMapping("/{id}")
|
||||
@ApiOperation("获取对象-字典绑定详细信息")
|
||||
public GenericsAjaxResult<ZoneObjectDict> getInfo(@PathVariable("id") Long id) {
|
||||
return genericsSuccess(zoneObjectDictService.selectZoneObjectDictById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某对象的全部字典绑定(含字典标签)
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:query")
|
||||
@GetMapping("/object/{objectType}/{objectId}")
|
||||
@ApiOperation("查询某对象的全部字典绑定(含字典标签)")
|
||||
public GenericsAjaxResult<List<ZoneObjectDict>> getByObject(@PathVariable("objectType") String objectType,
|
||||
@PathVariable("objectId") Long objectId) {
|
||||
return genericsSuccess(zoneObjectDictService.selectByObject(objectType, objectId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 反查:某字典数据关联的对象主键列表
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:query")
|
||||
@GetMapping("/objectIds")
|
||||
@ApiOperation("反查:某字典数据关联的对象主键列表")
|
||||
public GenericsAjaxResult<List<Long>> objectIds(@RequestParam("objectType") String objectType,
|
||||
@RequestParam("dictType") String dictType,
|
||||
@RequestParam("dictValue") String dictValue) {
|
||||
return genericsSuccess(zoneObjectDictService.selectObjectIdsByDict(objectType, dictType, dictValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象类型列表
|
||||
*/
|
||||
@GetMapping("/objectType/list")
|
||||
@ApiOperation("对象类型列表")
|
||||
public List<CommonTypeVo> objectTypeList() {
|
||||
return zoneObjectDictService.selectObjectTypeList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量保存某对象的字典绑定(传空列表清空)
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:add")
|
||||
@Log(title = "专区对象-字典扩展", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@ApiOperation("全量保存某对象的字典绑定(传空列表清空)")
|
||||
public AjaxResult save(@RequestBody @Validated ZoneObjectDictInputVo inputVo) {
|
||||
zoneObjectDictService.saveObjectDicts(inputVo);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据多个字典条件查询对象主键列表(AND 逻辑)
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:query")
|
||||
@PostMapping("/objectIdsByFilters")
|
||||
@ApiOperation("根据多个字典条件查询对象主键列表")
|
||||
public GenericsAjaxResult<List<Long>> objectIdsByFilters(@RequestParam("objectType") String objectType,
|
||||
@RequestBody JSONArray dictFiltersJson) {
|
||||
List<DictFilterVo> dictFilters = dictFiltersJson.toJavaList(DictFilterVo.class);
|
||||
return genericsSuccess(zoneObjectDictService.selectObjectIdsByDictFilters(objectType, dictFilters));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询多个对象的字典属性(含标签,用于跨服务按需加载)
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:query")
|
||||
@PostMapping("/dictsByObjectIds")
|
||||
@ApiOperation("批量查询多个对象的字典属性")
|
||||
public GenericsAjaxResult<List<JSONObject>> dictsByObjectIds(@RequestParam("objectType") String objectType,
|
||||
@RequestBody Long[] objectIds) {
|
||||
return genericsSuccess(
|
||||
zoneObjectDictService.selectDictAttributesByObjectIds(objectType, Arrays.asList(objectIds)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除对象-字典绑定
|
||||
*/
|
||||
// @RequiresPermissions("zone:objectDict:remove")
|
||||
@Log(title = "专区对象-字典扩展", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
@ApiOperation("批量删除对象-字典绑定")
|
||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||
return toAjax(zoneObjectDictService.deleteZoneObjectDictByIds(ids));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.microservices.zone.extension.domain;
|
||||
|
||||
import com.microservices.zone.detail.domain.ZoneSort;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 特色专区对象-字典扩展关联对象 zone_object_dict
|
||||
* <p>
|
||||
* 用于将专区对象(文章/资源/会员/项目)与系统字典(sys_dict_type/sys_dict_data)绑定,
|
||||
* 从而为专区对象扩展任意数量的自定义属性。
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("专区对象-字典扩展关联对象")
|
||||
public class ZoneObjectDict extends ZoneSort {
|
||||
public static final String table_name = "zone_object_dict";
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@ApiModelProperty(value = "主键")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 对象类型(CMS_DOC文章/RESOURCE资源/MEMBER会员/PROJECT项目)
|
||||
*/
|
||||
@ApiModelProperty(value = "对象类型(CMS_DOC文章/RESOURCE资源/MEMBER会员/PROJECT项目)")
|
||||
private String objectType;
|
||||
|
||||
/**
|
||||
* 对象主键(如 cms_doc.id / zone_resource.id 等)
|
||||
*/
|
||||
@ApiModelProperty(value = "对象主键")
|
||||
private Long objectId;
|
||||
|
||||
/**
|
||||
* 关联字典类型Key(sys_dict_type.dict_type)
|
||||
*/
|
||||
@ApiModelProperty(value = "关联字典类型Key")
|
||||
private String dictType;
|
||||
|
||||
/**
|
||||
* 关联字典数据键值(sys_dict_data.dict_value)
|
||||
*/
|
||||
@ApiModelProperty(value = "关联字典数据键值")
|
||||
private String dictValue;
|
||||
|
||||
/**
|
||||
* 字典数据标签(非持久化,服务端经 DictUtils 缓存解析填充)
|
||||
*/
|
||||
@ApiModelProperty(value = "字典数据标签(非持久化,服务端解析填充)")
|
||||
private String dictLabel;
|
||||
|
||||
/**
|
||||
* 字典类型名称(非持久化,服务端解析填充,来自 sys_dict_type.dict_name)
|
||||
*/
|
||||
@ApiModelProperty(value = "字典类型名称(非持久化,服务端解析填充)")
|
||||
private String dictTypeName;
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.microservices.zone.extension.domain;
|
||||
|
||||
import com.microservices.common.core.exception.ServiceException;
|
||||
import com.microservices.common.core.utils.StringUtils;
|
||||
import com.microservices.zone.specialProject.domain.vo.CommonTypeVo;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 专区对象字典扩展-对象类型枚举
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Getter
|
||||
@ApiModel("专区对象字典扩展-对象类型")
|
||||
public enum ZoneObjectDictTypeEnum {
|
||||
CMS_DOC("CMS_DOC", "文章"),
|
||||
RESOURCE("RESOURCE", "资源"),
|
||||
MEMBER("MEMBER", "会员"),
|
||||
PROJECT("PROJECT", "项目");
|
||||
|
||||
private final String key;
|
||||
private final String name;
|
||||
|
||||
ZoneObjectDictTypeEnum(String key, String name) {
|
||||
this.key = key;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static List<CommonTypeVo> getAllToList() {
|
||||
List<CommonTypeVo> list = new ArrayList<>();
|
||||
for (ZoneObjectDictTypeEnum e : values()) {
|
||||
list.add(new CommonTypeVo(e.key, e.name));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static String getNameByKey(String key) {
|
||||
if (StringUtils.isNotEmpty(key)) {
|
||||
for (ZoneObjectDictTypeEnum e : values()) {
|
||||
if (e.getKey().equalsIgnoreCase(key)) {
|
||||
return e.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ServiceException("该对象类型不存在(对象类型[%s])", key);
|
||||
}
|
||||
|
||||
public static boolean isValid(String key) {
|
||||
if (StringUtils.isNotEmpty(key)) {
|
||||
for (ZoneObjectDictTypeEnum e : values()) {
|
||||
if (e.getKey().equalsIgnoreCase(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.microservices.zone.extension.domain.vo;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 字典筛选条件VO
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
public class DictFilterVo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank(message = "字典类型不能为空")
|
||||
private String dictType;
|
||||
|
||||
@NotBlank(message = "字典键值不能为空")
|
||||
private String dictValue;
|
||||
|
||||
public String getDictType() {
|
||||
return dictType;
|
||||
}
|
||||
|
||||
public void setDictType(String dictType) {
|
||||
this.dictType = dictType;
|
||||
}
|
||||
|
||||
public String getDictValue() {
|
||||
return dictValue;
|
||||
}
|
||||
|
||||
public void setDictValue(String dictValue) {
|
||||
this.dictValue = dictValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.microservices.zone.extension.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 专区对象字典扩展-字典绑定项
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("专区对象字典扩展-字典绑定项")
|
||||
public class ZoneObjectDictBindingVo {
|
||||
|
||||
@ApiModelProperty(value = "关联字典类型Key", required = true)
|
||||
@NotBlank
|
||||
private String dictType;
|
||||
|
||||
@ApiModelProperty(value = "关联字典数据键值", required = true)
|
||||
@NotBlank
|
||||
private String dictValue;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.microservices.zone.extension.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 专区对象字典扩展-保存输入对象
|
||||
* <p>
|
||||
* 全量保存某对象的字典绑定:服务端会先删除该对象已有的全部绑定,再按 bindings 批量插入。
|
||||
* bindings 为空列表表示清空该对象的全部绑定。
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("专区对象字典扩展-保存输入对象")
|
||||
public class ZoneObjectDictInputVo {
|
||||
|
||||
@ApiModelProperty(value = "对象类型(CMS_DOC/RESOURCE/MEMBER/PROJECT)", required = true)
|
||||
@NotBlank
|
||||
private String objectType;
|
||||
|
||||
@ApiModelProperty(value = "对象主键", required = true)
|
||||
@NotNull
|
||||
private Long objectId;
|
||||
|
||||
@ApiModelProperty(value = "专区标识")
|
||||
private Long zoneId;
|
||||
|
||||
@ApiModelProperty(value = "所属组织Id")
|
||||
private Long deptId;
|
||||
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty(value = "字典绑定列表(传空列表表示清空该对象的所有绑定)", required = true)
|
||||
@NotNull
|
||||
@Valid
|
||||
private List<ZoneObjectDictBindingVo> bindings;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.microservices.zone.extension.domain.vo;
|
||||
|
||||
import com.microservices.common.core.utils.bean.BeanUtils;
|
||||
import com.microservices.zone.extension.domain.ZoneObjectDict;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 专区对象字典扩展-搜索对象
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("专区对象字典扩展-搜索对象")
|
||||
public class ZoneObjectDictSearchVo {
|
||||
|
||||
@ApiModelProperty(value = "对象类型")
|
||||
private String objectType;
|
||||
|
||||
@ApiModelProperty(value = "对象主键")
|
||||
private Long objectId;
|
||||
|
||||
@ApiModelProperty(value = "关联字典类型Key")
|
||||
private String dictType;
|
||||
|
||||
@ApiModelProperty(value = "关联字典数据键值")
|
||||
private String dictValue;
|
||||
|
||||
@ApiModelProperty(value = "专区标识")
|
||||
private Long zoneId;
|
||||
|
||||
@ApiModelProperty(value = "所属组织Id")
|
||||
private Long deptId;
|
||||
|
||||
public ZoneObjectDict toZoneObjectDict() {
|
||||
ZoneObjectDict target = new ZoneObjectDict();
|
||||
BeanUtils.copyProperties(this, target);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.microservices.zone.extension.mapper;
|
||||
|
||||
import com.microservices.zone.extension.domain.ZoneObjectDict;
|
||||
import com.microservices.zone.extension.domain.vo.DictFilterVo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 专区对象-字典扩展关联Mapper接口
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Mapper
|
||||
public interface ZoneObjectDictMapper {
|
||||
/**
|
||||
* 按主键查询
|
||||
*/
|
||||
ZoneObjectDict selectZoneObjectDictById(Long id);
|
||||
|
||||
/**
|
||||
* 条件查询列表
|
||||
*/
|
||||
List<ZoneObjectDict> selectZoneObjectDictList(ZoneObjectDict zoneObjectDict);
|
||||
|
||||
/**
|
||||
* 查询某对象的全部字典绑定
|
||||
*/
|
||||
List<ZoneObjectDict> selectByObject(@Param("objectType") String objectType, @Param("objectId") Long objectId);
|
||||
|
||||
/**
|
||||
* 批量查询多个对象的全部字典绑定
|
||||
*/
|
||||
List<ZoneObjectDict> selectByObjectIds(@Param("objectType") String objectType,
|
||||
@Param("objectIds") List<Long> objectIds);
|
||||
|
||||
/**
|
||||
* 反查:某字典数据关联的对象主键列表
|
||||
*/
|
||||
List<Long> selectObjectIdsByDict(@Param("objectType") String objectType,
|
||||
@Param("dictType") String dictType,
|
||||
@Param("dictValue") String dictValue);
|
||||
|
||||
/**
|
||||
* 新增单条
|
||||
*/
|
||||
int insertZoneObjectDict(ZoneObjectDict zoneObjectDict);
|
||||
|
||||
/**
|
||||
* 批量新增
|
||||
*/
|
||||
int batchInsertZoneObjectDict(@Param("list") List<ZoneObjectDict> list);
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*/
|
||||
int updateZoneObjectDict(ZoneObjectDict zoneObjectDict);
|
||||
|
||||
/**
|
||||
* 按主键删除
|
||||
*/
|
||||
int deleteZoneObjectDictById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*/
|
||||
int deleteZoneObjectDictByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 删除某对象的全部字典绑定(全量替换时使用)
|
||||
*/
|
||||
int deleteByObject(@Param("objectType") String objectType, @Param("objectId") Long objectId);
|
||||
|
||||
/**
|
||||
* 根据多个字典条件查询对象主键列表(AND 逻辑)
|
||||
* 返回同时满足所有字典条件的对象 ID
|
||||
*/
|
||||
List<Long> selectObjectIdsByDictFilters(@Param("objectType") String objectType,
|
||||
@Param("dictFilters") List<DictFilterVo> dictFilters);
|
||||
|
||||
/**
|
||||
* 批量查询字典类型名称(查 sys_dict_type),返回 dict_type / dict_name
|
||||
*/
|
||||
List<Map<String, String>> selectDictTypeNamesByTypes(@Param("dictTypes") List<String> dictTypes);
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.microservices.zone.extension.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.microservices.zone.extension.domain.ZoneObjectDict;
|
||||
import com.microservices.zone.extension.domain.vo.DictFilterVo;
|
||||
import com.microservices.zone.extension.domain.vo.ZoneObjectDictInputVo;
|
||||
import com.microservices.zone.specialProject.domain.vo.CommonTypeVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 专区对象-字典扩展关联Service接口
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
public interface IZoneObjectDictService {
|
||||
/**
|
||||
* 按主键查询(含字典标签解析)
|
||||
*/
|
||||
ZoneObjectDict selectZoneObjectDictById(Long id);
|
||||
|
||||
/**
|
||||
* 条件查询列表(含字典标签解析)
|
||||
*/
|
||||
List<ZoneObjectDict> selectZoneObjectDictList(ZoneObjectDict zoneObjectDict);
|
||||
|
||||
/**
|
||||
* 查询某对象的全部字典绑定(含字典标签解析)
|
||||
*/
|
||||
List<ZoneObjectDict> selectByObject(String objectType, Long objectId);
|
||||
|
||||
/**
|
||||
* 反查:某字典数据关联的对象主键列表
|
||||
*/
|
||||
List<Long> selectObjectIdsByDict(String objectType, String dictType, String dictValue);
|
||||
|
||||
/**
|
||||
* 全量保存某对象的字典绑定(先删后插,事务)。bindings 为空表示清空。
|
||||
*
|
||||
* @return 实际写入的绑定数量
|
||||
*/
|
||||
int saveObjectDicts(ZoneObjectDictInputVo inputVo);
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*/
|
||||
int deleteZoneObjectDictByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 按主键删除
|
||||
*/
|
||||
int deleteZoneObjectDictById(Long id);
|
||||
|
||||
/**
|
||||
* 对象类型列表
|
||||
*/
|
||||
List<CommonTypeVo> selectObjectTypeList();
|
||||
|
||||
/**
|
||||
* 根据多个字典条件查询对象主键列表(AND 逻辑)
|
||||
*/
|
||||
List<Long> selectObjectIdsByDictFilters(String objectType, List<DictFilterVo> dictFilters);
|
||||
|
||||
/**
|
||||
* 批量查询多个对象的字典属性(含标签解析),返回精简 JSONObject 列表。
|
||||
* 每条含:objectId / dictType / dictValue / dictLabel
|
||||
*/
|
||||
List<JSONObject> selectDictAttributesByObjectIds(String objectType, List<Long> objectIds);
|
||||
}
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
package com.microservices.zone.extension.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.microservices.common.core.exception.ServiceException;
|
||||
import com.microservices.common.core.utils.DateUtils;
|
||||
import com.microservices.common.core.utils.StringUtils;
|
||||
import com.microservices.common.security.utils.DictUtils;
|
||||
import com.microservices.common.security.utils.SecurityUtils;
|
||||
import com.microservices.system.api.domain.SysDictData;
|
||||
import com.microservices.zone.extension.domain.ZoneObjectDict;
|
||||
import com.microservices.zone.extension.domain.ZoneObjectDictTypeEnum;
|
||||
import com.microservices.zone.extension.domain.vo.DictFilterVo;
|
||||
import com.microservices.zone.extension.domain.vo.ZoneObjectDictBindingVo;
|
||||
import com.microservices.zone.extension.domain.vo.ZoneObjectDictInputVo;
|
||||
import com.microservices.zone.extension.mapper.ZoneObjectDictMapper;
|
||||
import com.microservices.zone.extension.service.IZoneObjectDictService;
|
||||
import com.microservices.zone.specialProject.domain.vo.CommonTypeVo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 专区对象-字典扩展关联Service业务层处理
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Service
|
||||
public class ZoneObjectDictServiceImpl implements IZoneObjectDictService {
|
||||
|
||||
@Autowired
|
||||
private ZoneObjectDictMapper zoneObjectDictMapper;
|
||||
|
||||
@Override
|
||||
public ZoneObjectDict selectZoneObjectDictById(Long id) {
|
||||
ZoneObjectDict zoneObjectDict = zoneObjectDictMapper.selectZoneObjectDictById(id);
|
||||
if (zoneObjectDict == null) {
|
||||
throw new ServiceException("该对象字典绑定不存在或无权限访问(主键id[" + id + "])");
|
||||
}
|
||||
resolveDictLabel(zoneObjectDict);
|
||||
return zoneObjectDict;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZoneObjectDict> selectZoneObjectDictList(ZoneObjectDict zoneObjectDict) {
|
||||
List<ZoneObjectDict> list = zoneObjectDictMapper.selectZoneObjectDictList(zoneObjectDict);
|
||||
resolveDictLabels(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZoneObjectDict> selectByObject(String objectType, Long objectId) {
|
||||
checkObjectType(objectType);
|
||||
if (objectId == null) {
|
||||
throw new ServiceException("对象主键不能为空");
|
||||
}
|
||||
List<ZoneObjectDict> list = zoneObjectDictMapper.selectByObject(objectType, objectId);
|
||||
resolveDictLabels(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectObjectIdsByDict(String objectType, String dictType, String dictValue) {
|
||||
checkObjectType(objectType);
|
||||
if (StringUtils.isEmpty(dictType) || StringUtils.isEmpty(dictValue)) {
|
||||
throw new ServiceException("字典类型Key与字典数据键值不能为空");
|
||||
}
|
||||
return zoneObjectDictMapper.selectObjectIdsByDict(objectType, dictType, dictValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int saveObjectDicts(ZoneObjectDictInputVo inputVo) {
|
||||
String objectType = inputVo.getObjectType();
|
||||
Long objectId = inputVo.getObjectId();
|
||||
checkObjectType(objectType);
|
||||
if (objectId == null) {
|
||||
throw new ServiceException("对象主键不能为空");
|
||||
}
|
||||
|
||||
List<ZoneObjectDictBindingVo> bindings = inputVo.getBindings();
|
||||
Set<String> seen = new HashSet<>();
|
||||
List<ZoneObjectDict> toInsert = new ArrayList<>();
|
||||
if (bindings != null) {
|
||||
for (ZoneObjectDictBindingVo binding : bindings) {
|
||||
String dictType = binding.getDictType();
|
||||
String dictValue = binding.getDictValue();
|
||||
if (StringUtils.isEmpty(dictType) || StringUtils.isEmpty(dictValue)) {
|
||||
throw new ServiceException("字典绑定项的字典类型Key与字典数据键值不能为空");
|
||||
}
|
||||
// 忽略同一字典数据的重复绑定
|
||||
if (!seen.add(dictType + ":" + dictValue)) {
|
||||
continue;
|
||||
}
|
||||
// 校验字典数据在缓存中真实存在
|
||||
checkDictData(dictType, dictValue);
|
||||
|
||||
ZoneObjectDict row = new ZoneObjectDict();
|
||||
row.setObjectType(objectType);
|
||||
row.setObjectId(objectId);
|
||||
row.setDictType(dictType);
|
||||
row.setDictValue(dictValue);
|
||||
row.setZoneId(inputVo.getZoneId());
|
||||
row.setDeptId(inputVo.getDeptId());
|
||||
row.setRemark(inputVo.getRemark());
|
||||
row.setDelFlag("0");
|
||||
row.setCreateBy(SecurityUtils.getUsername());
|
||||
row.setCreateTime(DateUtils.getNowDate());
|
||||
toInsert.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 全量替换:先删除该对象的全部绑定,再批量插入
|
||||
zoneObjectDictMapper.deleteByObject(objectType, objectId);
|
||||
if (!toInsert.isEmpty()) {
|
||||
zoneObjectDictMapper.batchInsertZoneObjectDict(toInsert);
|
||||
}
|
||||
return toInsert.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteZoneObjectDictByIds(Long[] ids) {
|
||||
return zoneObjectDictMapper.deleteZoneObjectDictByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteZoneObjectDictById(Long id) {
|
||||
return zoneObjectDictMapper.deleteZoneObjectDictById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommonTypeVo> selectObjectTypeList() {
|
||||
return ZoneObjectDictTypeEnum.getAllToList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectObjectIdsByDictFilters(String objectType, List<DictFilterVo> dictFilters) {
|
||||
checkObjectType(objectType);
|
||||
if (dictFilters == null || dictFilters.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 过滤掉字典数据不存在的筛选条件(静默跳过,不抛出异常)
|
||||
List<DictFilterVo> validFilters = new ArrayList<>();
|
||||
for (DictFilterVo filter : dictFilters) {
|
||||
if (isDictDataExists(filter.getDictType(), filter.getDictValue())) {
|
||||
validFilters.add(filter);
|
||||
}
|
||||
}
|
||||
// 如果所有字典条件都不存在,返回空列表
|
||||
if (validFilters.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return zoneObjectDictMapper.selectObjectIdsByDictFilters(objectType, validFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JSONObject> selectDictAttributesByObjectIds(String objectType, List<Long> objectIds) {
|
||||
checkObjectType(objectType);
|
||||
if (objectIds == null || objectIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<ZoneObjectDict> list = zoneObjectDictMapper.selectByObjectIds(objectType, objectIds);
|
||||
resolveDictLabels(list);
|
||||
return toDictAttributeJsonList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 ZoneObjectDict 转为精简 JSONObject(只含 objectId/dictType/dictValue/dictLabel/dictTypeName,
|
||||
* 不暴露 deptId/zoneId/createBy 等内部字段)
|
||||
*/
|
||||
private List<JSONObject> toDictAttributeJsonList(List<ZoneObjectDict> list) {
|
||||
List<JSONObject> result = new ArrayList<>(list.size());
|
||||
for (ZoneObjectDict zod : list) {
|
||||
JSONObject j = new JSONObject();
|
||||
j.put("objectId", zod.getObjectId());
|
||||
j.put("dictType", zod.getDictType());
|
||||
j.put("dictLabel", zod.getDictLabel());
|
||||
j.put("dictValue", zod.getDictValue());
|
||||
j.put("dictTypeName", zod.getDictTypeName());
|
||||
result.add(j);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===== private helpers =====
|
||||
|
||||
private void checkObjectType(String objectType) {
|
||||
if (!ZoneObjectDictTypeEnum.isValid(objectType)) {
|
||||
throw new ServiceException("不支持的对象类型(对象类型[%s])", objectType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验字典数据在 DictUtils 缓存中真实存在(按 dictType 取列表,再按 dictValue 过滤)
|
||||
*/
|
||||
private void checkDictData(String dictType, String dictValue) {
|
||||
List<SysDictData> dictDataList = DictUtils.getDictCache(dictType);
|
||||
if (dictDataList == null || dictDataList.isEmpty()) {
|
||||
throw new ServiceException("字典类型[%s]的字典数据缓存不存在,请先在系统字典中维护并刷新缓存", dictType);
|
||||
}
|
||||
boolean exists = dictDataList.stream().anyMatch(x -> dictValue.equals(x.getDictValue()));
|
||||
if (!exists) {
|
||||
throw new ServiceException("字典数据不存在(字典类型[%s],字典键值[%s])", dictType, dictValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字典数据是否存在(不抛出异常)
|
||||
* @param dictType 字典类型Key
|
||||
* @param dictValue 字典数据键值
|
||||
* @return true-存在,false-不存在
|
||||
*/
|
||||
private boolean isDictDataExists(String dictType, String dictValue) {
|
||||
if (StringUtils.isEmpty(dictType) || StringUtils.isEmpty(dictValue)) {
|
||||
return false;
|
||||
}
|
||||
List<SysDictData> dictDataList = DictUtils.getDictCache(dictType);
|
||||
if (dictDataList == null || dictDataList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return dictDataList.stream().anyMatch(x -> dictValue.equals(x.getDictValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单条绑定的字典标签(经 DictUtils 缓存,按 dictValue 过滤)
|
||||
*/
|
||||
private void resolveDictLabel(ZoneObjectDict zoneObjectDict) {
|
||||
if (zoneObjectDict == null
|
||||
|| StringUtils.isEmpty(zoneObjectDict.getDictType())
|
||||
|| StringUtils.isEmpty(zoneObjectDict.getDictValue())) {
|
||||
return;
|
||||
}
|
||||
List<SysDictData> dictDataList = DictUtils.getDictCache(zoneObjectDict.getDictType());
|
||||
if (dictDataList == null) {
|
||||
return;
|
||||
}
|
||||
dictDataList.stream()
|
||||
.filter(x -> zoneObjectDict.getDictValue().equals(x.getDictValue()))
|
||||
.findFirst()
|
||||
.ifPresent(d -> zoneObjectDict.setDictLabel(d.getDictLabel()));
|
||||
Map<String, String> typeNameMap = loadDictTypeNames(Collections.singleton(zoneObjectDict.getDictType()));
|
||||
zoneObjectDict.setDictTypeName(typeNameMap.get(zoneObjectDict.getDictType()));
|
||||
}
|
||||
|
||||
private void resolveDictLabels(List<ZoneObjectDict> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 按 dictType 去重,每种只取一次缓存,构建 (dictType|dictValue) -> SysDictData 查表
|
||||
Set<String> dictTypes = list.stream()
|
||||
.map(ZoneObjectDict::getDictType)
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, SysDictData> lookup = new HashMap<>();
|
||||
for (String dictType : dictTypes) {
|
||||
List<SysDictData> dictDataList = DictUtils.getDictCache(dictType);
|
||||
if (dictDataList == null) {
|
||||
continue;
|
||||
}
|
||||
for (SysDictData d : dictDataList) {
|
||||
lookup.put(dictType + "|" + d.getDictValue(), d);
|
||||
}
|
||||
}
|
||||
// 批量加载字典类型名称
|
||||
Map<String, String> typeNameMap = loadDictTypeNames(dictTypes);
|
||||
for (ZoneObjectDict zod : list) {
|
||||
if (zod.getDictType() == null || StringUtils.isEmpty(zod.getDictValue())) {
|
||||
continue;
|
||||
}
|
||||
SysDictData d = lookup.get(zod.getDictType() + "|" + zod.getDictValue());
|
||||
if (d != null) {
|
||||
zod.setDictLabel(d.getDictLabel());
|
||||
}
|
||||
zod.setDictTypeName(typeNameMap.get(zod.getDictType()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量加载字典类型名称(查 sys_dict_type),返回 dictType -> dictName
|
||||
*/
|
||||
private Map<String, String> loadDictTypeNames(Set<String> dictTypes) {
|
||||
if (dictTypes == null || dictTypes.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<Map<String, String>> rows = zoneObjectDictMapper.selectDictTypeNamesByTypes(new ArrayList<>(dictTypes));
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Map<String, String> row : rows) {
|
||||
map.put(row.get("dict_type"), row.get("dict_name"));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.microservices.zone.extension.mapper.ZoneObjectDictMapper">
|
||||
|
||||
<resultMap type="com.microservices.zone.extension.domain.ZoneObjectDict" id="ZoneObjectDictResult">
|
||||
<result property="id" column="id"/>
|
||||
<result property="objectType" column="object_type"/>
|
||||
<result property="objectId" column="object_id"/>
|
||||
<result property="dictType" column="dict_type"/>
|
||||
<result property="dictValue" column="dict_value"/>
|
||||
<result property="sort" column="sort"/>
|
||||
<result property="zoneId" column="zone_id"/>
|
||||
<result property="deptId" column="dept_id"/>
|
||||
<result property="delFlag" column="del_flag"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectZoneObjectDictVo">
|
||||
select id, object_type, object_id, dict_type, dict_value,
|
||||
sort, zone_id, dept_id, del_flag,
|
||||
create_by, create_time, update_by, update_time, remark
|
||||
from zone_object_dict
|
||||
</sql>
|
||||
|
||||
<select id="selectZoneObjectDictList"
|
||||
parameterType="com.microservices.zone.extension.domain.ZoneObjectDict"
|
||||
resultMap="ZoneObjectDictResult">
|
||||
<include refid="selectZoneObjectDictVo"/>
|
||||
<where>
|
||||
<if test="objectType != null and objectType != ''">and object_type = #{objectType}</if>
|
||||
<if test="objectId != null">and object_id = #{objectId}</if>
|
||||
<if test="dictType != null and dictType != ''">and dict_type = #{dictType}</if>
|
||||
<if test="dictValue != null and dictValue != ''">and dict_value = #{dictValue}</if>
|
||||
<if test="zoneId != null">and zone_id = #{zoneId}</if>
|
||||
<if test="deptId != null">and dept_id = #{deptId}</if>
|
||||
</where>
|
||||
order by id desc
|
||||
</select>
|
||||
|
||||
<select id="selectZoneObjectDictById" parameterType="Long" resultMap="ZoneObjectDictResult">
|
||||
<include refid="selectZoneObjectDictVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectByObject" resultMap="ZoneObjectDictResult">
|
||||
<include refid="selectZoneObjectDictVo"/>
|
||||
where object_type = #{objectType} and object_id = #{objectId}
|
||||
order by dict_type asc, id asc
|
||||
</select>
|
||||
|
||||
<select id="selectByObjectIds" resultMap="ZoneObjectDictResult">
|
||||
<include refid="selectZoneObjectDictVo"/>
|
||||
where object_type = #{objectType} and object_id in
|
||||
<foreach collection="objectIds" item="objectId" open="(" separator="," close=")">
|
||||
#{objectId}
|
||||
</foreach>
|
||||
order by object_id asc, dict_type asc, id asc
|
||||
</select>
|
||||
|
||||
<select id="selectObjectIdsByDict" resultType="long">
|
||||
select object_id
|
||||
from zone_object_dict
|
||||
where object_type = #{objectType}
|
||||
and dict_type = #{dictType}
|
||||
and dict_value = #{dictValue}
|
||||
order by object_id asc
|
||||
</select>
|
||||
|
||||
<insert id="insertZoneObjectDict"
|
||||
parameterType="com.microservices.zone.extension.domain.ZoneObjectDict"
|
||||
useGeneratedKeys="true"
|
||||
keyProperty="id">
|
||||
insert into zone_object_dict
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="objectType != null and objectType != ''">object_type,</if>
|
||||
<if test="objectId != null">object_id,</if>
|
||||
<if test="dictType != null and dictType != ''">dict_type,</if>
|
||||
<if test="dictValue != null and dictValue != ''">dict_value,</if>
|
||||
<if test="sort != null">sort,</if>
|
||||
<if test="zoneId != null">zone_id,</if>
|
||||
<if test="deptId != null">dept_id,</if>
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="objectType != null and objectType != ''">#{objectType},</if>
|
||||
<if test="objectId != null">#{objectId},</if>
|
||||
<if test="dictType != null and dictType != ''">#{dictType},</if>
|
||||
<if test="dictValue != null and dictValue != ''">#{dictValue},</if>
|
||||
<if test="sort != null">#{sort},</if>
|
||||
<if test="zoneId != null">#{zoneId},</if>
|
||||
<if test="deptId != null">#{deptId},</if>
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<insert id="batchInsertZoneObjectDict" parameterType="java.util.List">
|
||||
insert into zone_object_dict
|
||||
(object_type, object_id, dict_type, dict_value, sort, zone_id, dept_id, del_flag, create_by, create_time,
|
||||
update_by, update_time, remark)
|
||||
values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.objectType}, #{item.objectId}, #{item.dictType}, #{item.dictValue}, #{item.sort}, #{item.zoneId},
|
||||
#{item.deptId}, #{item.delFlag}, #{item.createBy}, #{item.createTime}, #{item.updateBy},
|
||||
#{item.updateTime}, #{item.remark})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<update id="updateZoneObjectDict"
|
||||
parameterType="com.microservices.zone.extension.domain.ZoneObjectDict">
|
||||
update zone_object_dict
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="objectType != null and objectType != ''">object_type = #{objectType},</if>
|
||||
<if test="objectId != null">object_id = #{objectId},</if>
|
||||
<if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
|
||||
<if test="dictValue != null and dictValue != ''">dict_value = #{dictValue},</if>
|
||||
<if test="sort != null">sort = #{sort},</if>
|
||||
<if test="zoneId != null">zone_id = #{zoneId},</if>
|
||||
<if test="deptId != null">dept_id = #{deptId},</if>
|
||||
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteZoneObjectDictById" parameterType="Long">
|
||||
delete
|
||||
from zone_object_dict
|
||||
where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteZoneObjectDictByIds" parameterType="String">
|
||||
delete from zone_object_dict where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deleteByObject">
|
||||
delete
|
||||
from zone_object_dict
|
||||
where object_type = #{objectType}
|
||||
and object_id = #{objectId}
|
||||
</delete>
|
||||
|
||||
<select id="selectObjectIdsByDictFilters" resultType="long">
|
||||
SELECT DISTINCT object_id
|
||||
FROM zone_object_dict
|
||||
WHERE object_type = #{objectType}
|
||||
AND del_flag = '0'
|
||||
<foreach collection="dictFilters" item="filter" separator="">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM zone_object_dict zod2
|
||||
WHERE zod2.object_type = #{objectType}
|
||||
AND zod2.object_id = zone_object_dict.object_id
|
||||
AND zod2.dict_type = #{filter.dictType}
|
||||
AND zod2.dict_value = #{filter.dictValue}
|
||||
AND zod2.del_flag = '0'
|
||||
)
|
||||
</foreach>
|
||||
ORDER BY object_id ASC
|
||||
</select>
|
||||
|
||||
<select id="selectDictTypeNamesByTypes" resultType="java.util.Map">
|
||||
select dict_type, dict_name
|
||||
from sys_dict_type
|
||||
where dict_type in
|
||||
<foreach collection="dictTypes" item="dt" open="(" separator="," close=")">
|
||||
#{dt}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
-- 特色专区对象-字典扩展关联表
|
||||
-- 说明:zone 模块未集成数据库迁移框架,此文件仅作建表参考,需在目标库手动执行。
|
||||
-- 字典类型/数据来源于 system 微服务的 sys_dict_type / sys_dict_data,
|
||||
-- 本表通过 dict_type(String键) + dict_value(sys_dict_data.dict_value) 关联,解析标签时复用 DictUtils 的 Redis 缓存。
|
||||
CREATE TABLE IF NOT EXISTS zone_object_dict
|
||||
(
|
||||
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
object_type VARCHAR(32) NOT NULL COMMENT '对象类型(CMS_DOC文章/RESOURCE资源/MEMBER会员/PROJECT项目)',
|
||||
object_id BIGINT NOT NULL COMMENT '对象主键(如 cms_doc.id / zone_resource.id 等)',
|
||||
dict_type VARCHAR(100) NOT NULL COMMENT '关联字典类型Key(sys_dict_type.dict_type)',
|
||||
dict_value VARCHAR(100) NOT NULL COMMENT '关联字典数据键值(sys_dict_data.dict_value)',
|
||||
sort INT DEFAULT NULL COMMENT '排序',
|
||||
zone_id BIGINT DEFAULT NULL COMMENT '专区标识',
|
||||
dept_id BIGINT DEFAULT NULL COMMENT '所属组织Id',
|
||||
del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0存在 2删除)',
|
||||
create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_object_dict (object_type, object_id, dict_type, dict_value),
|
||||
KEY idx_object (object_type, object_id),
|
||||
KEY idx_dict (dict_type, dict_value)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT ='特色专区对象-字典扩展关联表';
|
||||
Loading…
Reference in New Issue