refactor(emergency): 宣教模块代码整理与问题修复
- 修复 GlobalUtils.getLoginUser() NPE 风险(send/approve/reject) - 批量操作改为前置校验+全量执行(batchPublish/batchTakeDown/batchDeleteResources) - 新增 deleteResource/batchDeleteResources,已发布资源不可删除 - 标签过滤 SQL 修复边界精确匹配(避免 id=2 误匹配 12/22) - 收藏/阅读改用 DuplicateKeyException 消除并发竞态 - fillResourceExtras/queryResourceList 批量查询分类名称,避免 N+1 - 新增 validateResource 校验内容类型、分类、标签合法性 - copyResource 补全 effectiveTime/expiryTime、版本记录、校验 - 审核下发/下架/删除增加状态机保护 - send() 使用 REQUIRES_NEW 事务隔离,确保批量推送单条失败互不影响 - 版本历史上限控制 MAX_VERSIONS=20 - 分类/标签树形重构:分类不再嵌套子分类,统一为分类+标签两级 - 清理废弃方法(listCategories/listAllCategoriesAndTags 等) - 批量删除分类/标签增加 @Transactional 事务保护 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+4
-2
@@ -6,6 +6,8 @@ import com.renkang.emergency.dto.PushBatchSendDTO;
|
|||||||
import com.renkang.emergency.dto.PushFilterDTO;
|
import com.renkang.emergency.dto.PushFilterDTO;
|
||||||
import com.renkang.emergency.entity.FirstAidResource;
|
import com.renkang.emergency.entity.FirstAidResource;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Description: 急救宣教推送Service
|
* @Description: 急救宣教推送Service
|
||||||
* @Author: jeecg-boot
|
* @Author: jeecg-boot
|
||||||
@@ -24,7 +26,7 @@ public interface IFirstAidPushService {
|
|||||||
void send(String resourceId, String orderId);
|
void send(String resourceId, String orderId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量推送
|
* 批量推送,返回 {total, success, failed} 结构
|
||||||
*/
|
*/
|
||||||
int batchSend(PushBatchSendDTO dto);
|
Map<String, Object> batchSend(PushBatchSendDTO dto);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -14,7 +14,6 @@ import org.jeecg.common.aspect.annotation.AutoLog;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,17 +80,15 @@ public class FirstAidPushController {
|
|||||||
* 一键推送(批量)
|
* 一键推送(批量)
|
||||||
*/
|
*/
|
||||||
@AutoLog(value = "急救宣教资源-批量推送")
|
@AutoLog(value = "急救宣教资源-批量推送")
|
||||||
@Operation(summary = "一键推送", description = "批量推送资源,支持指定ID列表或按筛选条件全量推送")
|
@Operation(summary = "一键推送", description = "批量推送资源,支持指定ID列表或按筛选条件全量推送,返回成功/失败明细")
|
||||||
@PostMapping(value = "/batchSend")
|
@PostMapping(value = "/batchSend")
|
||||||
public Result<Map<String, Object>> batchSend(@RequestBody PushBatchSendDTO dto) {
|
public Result<Map<String, Object>> batchSend(@RequestBody PushBatchSendDTO dto) {
|
||||||
if (dto.getOrderId() == null || dto.getOrderId().isEmpty()) {
|
if (dto.getOrderId() == null || dto.getOrderId().isEmpty()) {
|
||||||
return Result.error("工单ID不能为空");
|
return Result.error("工单ID不能为空");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int count = pushService.batchSend(dto);
|
Map<String, Object> result = pushService.batchSend(dto);
|
||||||
Map<String, Object> result = new HashMap<>();
|
return Result.OK(result);
|
||||||
result.put("count", count);
|
|
||||||
return Result.OK("已推送 " + count + " 条", result);
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return Result.error(e.getMessage());
|
return Result.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-7
@@ -58,13 +58,13 @@ public class FirstAidResourceCategoryController extends JeecgController<FirstAid
|
|||||||
return Result.OK(categoryService.listCategoriesWithTags());
|
return Result.OK(categoryService.listCategoriesWithTags());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "急救宣教资源分类-根分类列表")
|
@Operation(summary = "急救宣教资源分类-分类列表")
|
||||||
@GetMapping(value = "/listRoot")
|
@GetMapping(value = "/listRoot")
|
||||||
public Result<List<FirstAidResourceCategory>> listRoot() {
|
public Result<List<FirstAidResourceCategory>> listRoot() {
|
||||||
return Result.OK(categoryService.listRootCategories());
|
return Result.OK(categoryService.listRootCategories());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "急救宣教资源分类-子分类列表")
|
@Operation(summary = "急救宣教资源分类-标签列表(按分类)")
|
||||||
@GetMapping(value = "/listChildren")
|
@GetMapping(value = "/listChildren")
|
||||||
public Result<List<FirstAidResourceCategory>> listChildren(@RequestParam(name = "parentId") String parentId) {
|
public Result<List<FirstAidResourceCategory>> listChildren(@RequestParam(name = "parentId") String parentId) {
|
||||||
return Result.OK(categoryService.listChildrenByParentId(parentId));
|
return Result.OK(categoryService.listChildrenByParentId(parentId));
|
||||||
@@ -128,13 +128,10 @@ public class FirstAidResourceCategoryController extends JeecgController<FirstAid
|
|||||||
@AutoLog(value = "急救宣教资源分类-批量删除")
|
@AutoLog(value = "急救宣教资源分类-批量删除")
|
||||||
//@RequiresPermissions("emergency:firstAidCategory:deleteBatch")
|
//@RequiresPermissions("emergency:firstAidCategory:deleteBatch")
|
||||||
@PostMapping(value = "/deleteBatch")
|
@PostMapping(value = "/deleteBatch")
|
||||||
|
@org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class)
|
||||||
public Result<String> deleteBatch(@RequestBody List<String> ids) {
|
public Result<String> deleteBatch(@RequestBody List<String> ids) {
|
||||||
for (String id : ids) {
|
for (String id : ids) {
|
||||||
try {
|
categoryService.deleteWithCheck(id);
|
||||||
categoryService.deleteWithCheck(id);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
return Result.error("ID[" + id + "]:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Result.OK("批量删除成功!");
|
return Result.OK("批量删除成功!");
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-7
@@ -61,7 +61,8 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
entity.setStatus(status);
|
entity.setStatus(status);
|
||||||
entity.setTags(tags);
|
entity.setTags(tags);
|
||||||
Page<FirstAidResource> page = new Page<>(pageNo, pageSize);
|
Page<FirstAidResource> page = new Page<>(pageNo, pageSize);
|
||||||
String userId = GlobalUtils.getLoginUser().getId();
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
|
String userId = loginUser != null ? loginUser.getId() : null;
|
||||||
return Result.OK(resourceService.queryPageList(entity, page, userId));
|
return Result.OK(resourceService.queryPageList(entity, page, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +92,11 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
@AutoLog(value = "急救宣教资源-删除")
|
@AutoLog(value = "急救宣教资源-删除")
|
||||||
@PostMapping(value = "/delete")
|
@PostMapping(value = "/delete")
|
||||||
public Result<String> delete(@RequestBody IdDTO dto) {
|
public Result<String> delete(@RequestBody IdDTO dto) {
|
||||||
resourceService.removeById(dto.getId());
|
try {
|
||||||
|
resourceService.deleteResource(dto.getId());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
return Result.OK("删除成功!");
|
return Result.OK("删除成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +104,11 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
@AutoLog(value = "急救宣教资源-批量删除")
|
@AutoLog(value = "急救宣教资源-批量删除")
|
||||||
@PostMapping(value = "/deleteBatch")
|
@PostMapping(value = "/deleteBatch")
|
||||||
public Result<String> deleteBatch(@RequestBody List<String> ids) {
|
public Result<String> deleteBatch(@RequestBody List<String> ids) {
|
||||||
resourceService.removeByIds(ids);
|
try {
|
||||||
|
resourceService.batchDeleteResources(ids);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
return Result.OK("批量删除成功!");
|
return Result.OK("批量删除成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +116,11 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
@AutoLog(value = "急救宣教资源-批量发布")
|
@AutoLog(value = "急救宣教资源-批量发布")
|
||||||
@PostMapping(value = "/batchPublish")
|
@PostMapping(value = "/batchPublish")
|
||||||
public Result<String> batchPublish(@RequestBody List<String> ids) {
|
public Result<String> batchPublish(@RequestBody List<String> ids) {
|
||||||
resourceService.batchPublish(ids);
|
try {
|
||||||
|
resourceService.batchPublish(ids);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
return Result.OK("批量发布成功!");
|
return Result.OK("批量发布成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +128,11 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
@AutoLog(value = "急救宣教资源-批量下架")
|
@AutoLog(value = "急救宣教资源-批量下架")
|
||||||
@PostMapping(value = "/batchTakeDown")
|
@PostMapping(value = "/batchTakeDown")
|
||||||
public Result<String> batchTakeDown(@RequestBody List<String> ids) {
|
public Result<String> batchTakeDown(@RequestBody List<String> ids) {
|
||||||
resourceService.batchTakeDown(ids);
|
try {
|
||||||
|
resourceService.batchTakeDown(ids);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
return Result.OK("批量下架成功!");
|
return Result.OK("批量下架成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +175,8 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
@Operation(summary = "急救宣教资源-通过id查询详情")
|
@Operation(summary = "急救宣教资源-通过id查询详情")
|
||||||
@GetMapping(value = "/queryById")
|
@GetMapping(value = "/queryById")
|
||||||
public Result<FirstAidResource> queryById(@RequestParam(name = "id") String id) {
|
public Result<FirstAidResource> queryById(@RequestParam(name = "id") String id) {
|
||||||
String userId = GlobalUtils.getLoginUser().getId();
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
|
String userId = loginUser != null ? loginUser.getId() : null;
|
||||||
FirstAidResource entity = resourceService.getDetail(id, userId);
|
FirstAidResource entity = resourceService.getDetail(id, userId);
|
||||||
return entity == null ? Result.error("未找到对应数据") : Result.OK(entity);
|
return entity == null ? Result.error("未找到对应数据") : Result.OK(entity);
|
||||||
}
|
}
|
||||||
@@ -167,7 +185,8 @@ public class FirstAidResourceController extends JeecgController<FirstAidResource
|
|||||||
@AutoLog(value = "急救宣教资源-记录阅读")
|
@AutoLog(value = "急救宣教资源-记录阅读")
|
||||||
@PostMapping(value = "/incrementView")
|
@PostMapping(value = "/incrementView")
|
||||||
public Result<String> incrementView(@RequestBody IdDTO dto) {
|
public Result<String> incrementView(@RequestBody IdDTO dto) {
|
||||||
String userId = GlobalUtils.getLoginUser().getId();
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
|
String userId = loginUser != null ? loginUser.getId() : null;
|
||||||
boolean firstRead = resourceService.incrementViewCount(dto.getId(), userId);
|
boolean firstRead = resourceService.incrementViewCount(dto.getId(), userId);
|
||||||
return firstRead ? Result.OK("阅读记录成功") : Result.OK("已阅读过");
|
return firstRead ? Result.OK("阅读记录成功") : Result.OK("已阅读过");
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -128,13 +128,10 @@ public class FirstAidResourceTagController extends JeecgController<FirstAidResou
|
|||||||
@AutoLog(value = "急救宣教资源标签-批量删除")
|
@AutoLog(value = "急救宣教资源标签-批量删除")
|
||||||
//@RequiresPermissions("emergency:firstAidTag:deleteBatch")
|
//@RequiresPermissions("emergency:firstAidTag:deleteBatch")
|
||||||
@PostMapping(value = "/deleteBatch")
|
@PostMapping(value = "/deleteBatch")
|
||||||
|
@org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class)
|
||||||
public Result<String> deleteBatch(@RequestBody List<String> ids) {
|
public Result<String> deleteBatch(@RequestBody List<String> ids) {
|
||||||
for (String id : ids) {
|
for (String id : ids) {
|
||||||
try {
|
categoryService.deleteWithCheck(id);
|
||||||
categoryService.deleteWithCheck(id);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
return Result.error("ID[" + id + "]:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Result.OK("批量删除成功!");
|
return Result.OK("批量删除成功!");
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-17
@@ -18,9 +18,9 @@ import java.util.List;
|
|||||||
public interface FirstAidResourceCategoryMapper extends BaseMapper<FirstAidResourceCategory> {
|
public interface FirstAidResourceCategoryMapper extends BaseMapper<FirstAidResourceCategory> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询某分类下的子分类/标签数量(级联删除校验用,含TYPE=1子分类和TYPE=2标签)
|
* 查询某分类下的标签数量(级联删除校验用,TYPE=2)
|
||||||
*/
|
*/
|
||||||
@Select("SELECT COUNT(*) FROM QH_EME_FIRST_AID_RESOURCE_CATEGORY WHERE PARENT_ID = #{parentId} AND DEL_FLAG = 0")
|
@Select("SELECT COUNT(*) FROM QH_EME_FIRST_AID_RESOURCE_CATEGORY WHERE PARENT_ID = #{parentId} AND TYPE = 2 AND DEL_FLAG = 0")
|
||||||
Integer countChildrenByParentId(@Param("parentId") String parentId);
|
Integer countChildrenByParentId(@Param("parentId") String parentId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,19 +32,13 @@ public interface FirstAidResourceCategoryMapper extends BaseMapper<FirstAidResou
|
|||||||
/**
|
/**
|
||||||
* 查询引用某标签的资源数量(标签删除校验用)
|
* 查询引用某标签的资源数量(标签删除校验用)
|
||||||
*/
|
*/
|
||||||
@Select("SELECT COUNT(*) FROM QH_EME_FIRST_AID_RESOURCE WHERE DEL_FLAG = 0 AND TAGS LIKE CONCAT('%', #{tagId}, '%')")
|
@Select("SELECT COUNT(*) FROM QH_EME_FIRST_AID_RESOURCE WHERE DEL_FLAG = 0 AND CONCAT(',', TAGS, ',') LIKE CONCAT('%,', #{tagId}, ',%')")
|
||||||
Integer countResourcesByTagId(@Param("tagId") String tagId);
|
Integer countResourcesByTagId(@Param("tagId") String tagId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询所有有效分类和标签(树形组装用,含parentId)
|
* 按父分类ID查询标签列表(TYPE=2)
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM QH_EME_FIRST_AID_RESOURCE_CATEGORY WHERE STATUS = 1 AND DEL_FLAG = 0 ORDER BY TYPE ASC, SORT_NO ASC")
|
@Select("SELECT * FROM QH_EME_FIRST_AID_RESOURCE_CATEGORY WHERE PARENT_ID = #{parentId} AND TYPE = 2 AND STATUS = 1 AND DEL_FLAG = 0 ORDER BY SORT_NO ASC")
|
||||||
List<FirstAidResourceCategory> selectAllCategoriesAndTags();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 按父ID查询子节点(含子分类TYPE=1和标签TYPE=2)
|
|
||||||
*/
|
|
||||||
@Select("SELECT * FROM QH_EME_FIRST_AID_RESOURCE_CATEGORY WHERE PARENT_ID = #{parentId} AND STATUS = 1 AND DEL_FLAG = 0 ORDER BY TYPE ASC, SORT_NO ASC")
|
|
||||||
List<FirstAidResourceCategory> selectByParentId(@Param("parentId") String parentId);
|
List<FirstAidResourceCategory> selectByParentId(@Param("parentId") String parentId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,12 +57,6 @@ public interface FirstAidResourceCategoryMapper extends BaseMapper<FirstAidResou
|
|||||||
"</script>")
|
"</script>")
|
||||||
List<FirstAidResourceCategory> selectByTypeAndParent(@Param("type") Integer type, @Param("parentId") String parentId);
|
List<FirstAidResourceCategory> selectByTypeAndParent(@Param("type") Integer type, @Param("parentId") String parentId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 按父分类ID查询子分类列表(树形结构用)
|
|
||||||
*/
|
|
||||||
@Select("SELECT * FROM QH_EME_FIRST_AID_RESOURCE_CATEGORY WHERE PARENT_ID = #{parentId} AND TYPE = 1 AND STATUS = 1 AND DEL_FLAG = 0 ORDER BY SORT_NO ASC")
|
|
||||||
List<FirstAidResourceCategory> selectChildrenByParentId(@Param("parentId") String parentId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询所有根分类(PARENT_ID IS NULL)
|
* 查询所有根分类(PARENT_ID IS NULL)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
@@ -28,4 +28,10 @@ public interface FirstAidResourceVersionMapper extends BaseMapper<FirstAidResour
|
|||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM QH_EME_FIRST_AID_RESOURCE_VERSION WHERE RESOURCE_ID = #{resourceId} ORDER BY VERSION_NO DESC")
|
@Select("SELECT * FROM QH_EME_FIRST_AID_RESOURCE_VERSION WHERE RESOURCE_ID = #{resourceId} ORDER BY VERSION_NO DESC")
|
||||||
List<FirstAidResourceVersion> selectByResourceId(@Param("resourceId") String resourceId);
|
List<FirstAidResourceVersion> selectByResourceId(@Param("resourceId") String resourceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询某资源的所有非当前版本ID(按版本号降序),用于清理旧版本
|
||||||
|
*/
|
||||||
|
@Select("SELECT ID FROM QH_EME_FIRST_AID_RESOURCE_VERSION WHERE RESOURCE_ID = #{resourceId} AND IS_CURRENT = 0 ORDER BY VERSION_NO DESC")
|
||||||
|
List<String> selectNonCurrentVersionIds(@Param("resourceId") String resourceId);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-13
@@ -14,27 +14,17 @@ import java.util.List;
|
|||||||
public interface IFirstAidResourceCategoryService extends IService<FirstAidResourceCategory> {
|
public interface IFirstAidResourceCategoryService extends IService<FirstAidResourceCategory> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询所有有效分类(扁平,一级分类)
|
* 按父分类ID查询标签列表(TYPE=2)
|
||||||
*/
|
|
||||||
List<FirstAidResourceCategory> listCategories();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询所有分类和标签(扁平,通过parentId组装树)
|
|
||||||
*/
|
|
||||||
List<FirstAidResourceCategory> listAllCategoriesAndTags();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 按父ID查询子节点(含子分类和标签,树形展开用)
|
|
||||||
*/
|
*/
|
||||||
List<FirstAidResourceCategory> listByParentId(String parentId);
|
List<FirstAidResourceCategory> listByParentId(String parentId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询根分类列表(树形结构顶层)
|
* 查询所有分类(扁平,无子分类)
|
||||||
*/
|
*/
|
||||||
List<FirstAidResourceCategory> listRootCategories();
|
List<FirstAidResourceCategory> listRootCategories();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按父分类ID查询子分类
|
* 按父分类ID查询标签(树形展开用)
|
||||||
*/
|
*/
|
||||||
List<FirstAidResourceCategory> listChildrenByParentId(String parentId);
|
List<FirstAidResourceCategory> listChildrenByParentId(String parentId);
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -37,10 +37,10 @@ public interface IFirstAidResourceService extends IService<FirstAidResource> {
|
|||||||
/** 审核驳回 */
|
/** 审核驳回 */
|
||||||
void reject(String id, String rejectReason);
|
void reject(String id, String rejectReason);
|
||||||
|
|
||||||
/** 批量发布 */
|
/** 批量发布(前置校验:全部审核通过才执行,否则抛异常) */
|
||||||
void batchPublish(List<String> ids);
|
void batchPublish(List<String> ids);
|
||||||
|
|
||||||
/** 批量下架 */
|
/** 批量下架(前置校验:全部已发布才执行,否则抛异常) */
|
||||||
void batchTakeDown(List<String> ids);
|
void batchTakeDown(List<String> ids);
|
||||||
|
|
||||||
IPage<FirstAidResource> queryPageList(FirstAidResource resource, Page<FirstAidResource> page, String userId);
|
IPage<FirstAidResource> queryPageList(FirstAidResource resource, Page<FirstAidResource> page, String userId);
|
||||||
@@ -49,6 +49,12 @@ public interface IFirstAidResourceService extends IService<FirstAidResource> {
|
|||||||
|
|
||||||
FirstAidResource getDetail(String id, String userId);
|
FirstAidResource getDetail(String id, String userId);
|
||||||
|
|
||||||
|
/** 删除资源(已发布状态不可删除) */
|
||||||
|
void deleteResource(String id);
|
||||||
|
|
||||||
|
/** 批量删除资源(前置校验:全部非已发布才执行,否则抛异常) */
|
||||||
|
void batchDeleteResources(List<String> ids);
|
||||||
|
|
||||||
/** 批量转换实体为导出VO(含分类、标签翻译) */
|
/** 批量转换实体为导出VO(含分类、标签翻译) */
|
||||||
List<FirstAidResourceExportVO> convertToExportVO(List<FirstAidResource> resources);
|
List<FirstAidResourceExportVO> convertToExportVO(List<FirstAidResource> resources);
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-13
@@ -19,10 +19,12 @@ import com.renkang.emergency.mapper.OrderMapper;
|
|||||||
import com.renkang.emergency.service.IFirstAidPushService;
|
import com.renkang.emergency.service.IFirstAidPushService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jeecg.common.constant.CommonConstant;
|
import org.jeecg.common.constant.CommonConstant;
|
||||||
|
import org.jeecg.common.system.vo.LoginUser;
|
||||||
import org.jeecg.global.GlobalUtils;
|
import org.jeecg.global.GlobalUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -63,14 +65,22 @@ public class FirstAidPushServiceImpl implements IFirstAidPushService {
|
|||||||
// 查询当前工单已推送的资源ID集合
|
// 查询当前工单已推送的资源ID集合
|
||||||
Set<String> pushedIds = getPushedResourceIds(orderId);
|
Set<String> pushedIds = getPushedResourceIds(orderId);
|
||||||
|
|
||||||
|
// 批量查询分类名称,避免 N+1
|
||||||
|
List<String> categoryIds = result.getRecords().stream()
|
||||||
|
.map(FirstAidResource::getCategoryId)
|
||||||
|
.filter(StrUtil::isNotBlank)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
java.util.Map<String, String> categoryNameMap = CollectionUtil.isEmpty(categoryIds)
|
||||||
|
? Collections.emptyMap()
|
||||||
|
: categoryMapper.selectBatchIds(categoryIds).stream()
|
||||||
|
.collect(Collectors.toMap(FirstAidResourceCategory::getId, FirstAidResourceCategory::getName, (a, b) -> a));
|
||||||
|
|
||||||
// 填充推送状态 + 分类名称
|
// 填充推送状态 + 分类名称
|
||||||
for (FirstAidResource resource : result.getRecords()) {
|
for (FirstAidResource resource : result.getRecords()) {
|
||||||
resource.setPushed(pushedIds.contains(resource.getId()));
|
resource.setPushed(pushedIds.contains(resource.getId()));
|
||||||
if (StrUtil.isNotBlank(resource.getCategoryId())) {
|
if (StrUtil.isNotBlank(resource.getCategoryId())) {
|
||||||
FirstAidResourceCategory category = categoryMapper.selectById(resource.getCategoryId());
|
resource.setCategoryName(categoryNameMap.get(resource.getCategoryId()));
|
||||||
if (category != null) {
|
|
||||||
resource.setCategoryName(category.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +88,7 @@ public class FirstAidPushServiceImpl implements IFirstAidPushService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||||
public void send(String resourceId, String orderId) {
|
public void send(String resourceId, String orderId) {
|
||||||
// 1. 校验资源
|
// 1. 校验资源
|
||||||
FirstAidResource resource = resourceMapper.selectById(resourceId);
|
FirstAidResource resource = resourceMapper.selectById(resourceId);
|
||||||
@@ -108,25 +118,29 @@ public class FirstAidPushServiceImpl implements IFirstAidPushService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 保存推送记录
|
// 4. 保存推送记录
|
||||||
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
FirstAidPushRecord record = new FirstAidPushRecord();
|
FirstAidPushRecord record = new FirstAidPushRecord();
|
||||||
record.setResourceId(resourceId);
|
record.setResourceId(resourceId);
|
||||||
record.setResourceTitle(resource.getTitle());
|
record.setResourceTitle(resource.getTitle());
|
||||||
record.setOrderId(orderId);
|
record.setOrderId(orderId);
|
||||||
record.setTargetUserId(targetUserId);
|
record.setTargetUserId(targetUserId);
|
||||||
record.setPushBy(GlobalUtils.getLoginUser().getRealname());
|
if (loginUser != null) {
|
||||||
record.setPushById(GlobalUtils.getLoginUser().getId());
|
record.setPushBy(loginUser.getRealname());
|
||||||
|
record.setPushById(loginUser.getId());
|
||||||
|
}
|
||||||
record.setPushTime(new Date());
|
record.setPushTime(new Date());
|
||||||
pushRecordMapper.insert(record);
|
pushRecordMapper.insert(record);
|
||||||
|
|
||||||
// 5. 推送通知(预留,当前记录日志)
|
// 5. 推送通知(预留,当前记录日志)
|
||||||
log.info("急救宣教推送: resourceId={}, title={}, orderId={}, targetUserId={}, pushById={}, pushByName={}",
|
log.info("急救宣教推送: resourceId={}, title={}, orderId={}, targetUserId={}, pushById={}, pushByName={}",
|
||||||
resourceId, resource.getTitle(), orderId, targetUserId,
|
resourceId, resource.getTitle(), orderId, targetUserId,
|
||||||
GlobalUtils.getLoginUser().getId(), GlobalUtils.getLoginUser().getRealname());
|
loginUser != null ? loginUser.getId() : null,
|
||||||
|
loginUser != null ? loginUser.getRealname() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public int batchSend(PushBatchSendDTO dto) {
|
public Map<String, Object> batchSend(PushBatchSendDTO dto) {
|
||||||
List<String> resourceIds = dto.getResourceIds();
|
List<String> resourceIds = dto.getResourceIds();
|
||||||
|
|
||||||
// 方式二:按筛选条件查询
|
// 方式二:按筛选条件查询
|
||||||
@@ -141,7 +155,7 @@ public class FirstAidPushServiceImpl implements IFirstAidPushService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
List<String> errors = new ArrayList<>();
|
List<Map<String, String>> errors = new ArrayList<>();
|
||||||
|
|
||||||
for (String resourceId : resourceIds) {
|
for (String resourceId : resourceIds) {
|
||||||
try {
|
try {
|
||||||
@@ -149,19 +163,29 @@ public class FirstAidPushServiceImpl implements IFirstAidPushService {
|
|||||||
self.send(resourceId, dto.getOrderId());
|
self.send(resourceId, dto.getOrderId());
|
||||||
successCount++;
|
successCount++;
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
errors.add(resourceId + ":" + e.getMessage());
|
Map<String, String> err = new HashMap<>();
|
||||||
|
err.put("resourceId", resourceId);
|
||||||
|
err.put("message", e.getMessage());
|
||||||
|
errors.add(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
log.warn("批量推送部分失败: {}/{} 条, 失败原因: {}",
|
log.warn("批量推送部分失败: {}/{} 条, 失败原因: {}",
|
||||||
successCount, resourceIds.size(), String.join("; ", errors));
|
successCount, resourceIds.size(), errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
return successCount;
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("total", resourceIds.size());
|
||||||
|
result.put("success", successCount);
|
||||||
|
result.put("failed", errors);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getPushedResourceIds(String orderId) {
|
private Set<String> getPushedResourceIds(String orderId) {
|
||||||
|
if (StrUtil.isBlank(orderId)) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
LambdaQueryWrapper<FirstAidPushRecord> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<FirstAidPushRecord> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.eq(FirstAidPushRecord::getOrderId, orderId)
|
wrapper.eq(FirstAidPushRecord::getOrderId, orderId)
|
||||||
.select(FirstAidPushRecord::getResourceId);
|
.select(FirstAidPushRecord::getResourceId);
|
||||||
|
|||||||
+8
-32
@@ -2,6 +2,7 @@ package com.renkang.emergency.service.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.renkang.emergency.constant.FirstAidConstants;
|
import com.renkang.emergency.constant.FirstAidConstants;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import com.renkang.emergency.entity.FirstAidResourceCategory;
|
import com.renkang.emergency.entity.FirstAidResourceCategory;
|
||||||
import com.renkang.emergency.mapper.FirstAidResourceCategoryMapper;
|
import com.renkang.emergency.mapper.FirstAidResourceCategoryMapper;
|
||||||
import com.renkang.emergency.mapper.FirstAidResourceMapper;
|
import com.renkang.emergency.mapper.FirstAidResourceMapper;
|
||||||
@@ -26,39 +27,13 @@ public class FirstAidResourceCategoryServiceImpl extends ServiceImpl<FirstAidRes
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FirstAidResourceMapper resourceMapper;
|
private FirstAidResourceMapper resourceMapper;
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FirstAidResourceCategory> listCategories() {
|
|
||||||
List<FirstAidResourceCategory> list = categoryMapper.selectByType(FirstAidConstants.TYPE_CATEGORY);
|
|
||||||
fillResourceCounts(list);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FirstAidResourceCategory> listAllCategoriesAndTags() {
|
|
||||||
List<FirstAidResourceCategory> list = categoryMapper.selectAllCategoriesAndTags();
|
|
||||||
for (FirstAidResourceCategory item : list) {
|
|
||||||
if (FirstAidConstants.TYPE_CATEGORY.equals(item.getType())) {
|
|
||||||
Integer count = resourceMapper.countByCategoryId(item.getId());
|
|
||||||
item.setResourceCount(count != null ? count : 0);
|
|
||||||
} else {
|
|
||||||
Integer count = categoryMapper.countResourcesByTagId(item.getId());
|
|
||||||
item.setResourceCount(count != null ? count : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FirstAidResourceCategory> listByParentId(String parentId) {
|
public List<FirstAidResourceCategory> listByParentId(String parentId) {
|
||||||
|
// selectByParentId 仅查 TYPE=2(标签),无子分类
|
||||||
List<FirstAidResourceCategory> list = categoryMapper.selectByParentId(parentId);
|
List<FirstAidResourceCategory> list = categoryMapper.selectByParentId(parentId);
|
||||||
for (FirstAidResourceCategory item : list) {
|
for (FirstAidResourceCategory tag : list) {
|
||||||
if (FirstAidConstants.TYPE_CATEGORY.equals(item.getType())) {
|
Integer count = categoryMapper.countResourcesByTagId(tag.getId());
|
||||||
Integer count = resourceMapper.countByCategoryId(item.getId());
|
tag.setResourceCount(count != null ? count : 0);
|
||||||
item.setResourceCount(count != null ? count : 0);
|
|
||||||
} else {
|
|
||||||
Integer count = categoryMapper.countResourcesByTagId(item.getId());
|
|
||||||
item.setResourceCount(count != null ? count : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -117,6 +92,7 @@ public class FirstAidResourceCategoryServiceImpl extends ServiceImpl<FirstAidRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void deleteWithCheck(String id) {
|
public void deleteWithCheck(String id) {
|
||||||
FirstAidResourceCategory record = getById(id);
|
FirstAidResourceCategory record = getById(id);
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
@@ -125,8 +101,8 @@ public class FirstAidResourceCategoryServiceImpl extends ServiceImpl<FirstAidRes
|
|||||||
|
|
||||||
if (FirstAidConstants.TYPE_CATEGORY.equals(record.getType())) {
|
if (FirstAidConstants.TYPE_CATEGORY.equals(record.getType())) {
|
||||||
// 分类:检查是否有标签
|
// 分类:检查是否有标签
|
||||||
Integer childCount = categoryMapper.countChildrenByParentId(id);
|
Integer tagCount = categoryMapper.countChildrenByParentId(id);
|
||||||
if (childCount != null && childCount > 0) {
|
if (tagCount != null && tagCount > 0) {
|
||||||
throw new RuntimeException("该分类下存在标签,无法删除");
|
throw new RuntimeException("该分类下存在标签,无法删除");
|
||||||
}
|
}
|
||||||
// 分类:检查是否有资源
|
// 分类:检查是否有资源
|
||||||
|
|||||||
+154
-21
@@ -17,15 +17,19 @@ import com.renkang.emergency.service.IFirstAidResourceUserActionService;
|
|||||||
import com.renkang.emergency.service.IFirstAidResourceVersionService;
|
import com.renkang.emergency.service.IFirstAidResourceVersionService;
|
||||||
import com.renkang.emergency.vo.FirstAidResourceExportVO;
|
import com.renkang.emergency.vo.FirstAidResourceExportVO;
|
||||||
import org.jeecg.common.constant.CommonConstant;
|
import org.jeecg.common.constant.CommonConstant;
|
||||||
|
import org.jeecg.common.system.vo.LoginUser;
|
||||||
import org.jeecg.global.GlobalUtils;
|
import org.jeecg.global.GlobalUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,6 +67,7 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
}
|
}
|
||||||
resource.setStatus(FirstAidConstants.STATUS_DRAFT);
|
resource.setStatus(FirstAidConstants.STATUS_DRAFT);
|
||||||
resource.setAuditStatus(null);
|
resource.setAuditStatus(null);
|
||||||
|
validateResource(resource);
|
||||||
save(resource);
|
save(resource);
|
||||||
// 保存初始版本,标记为当前版本
|
// 保存初始版本,标记为当前版本
|
||||||
versionService.saveVersion(resource.getId(), resource.getTitle(), resource.getContent(), "初始版本", true);
|
versionService.saveVersion(resource.getId(), resource.getTitle(), resource.getContent(), "初始版本", true);
|
||||||
@@ -81,6 +86,7 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
resource.setStatus(FirstAidConstants.STATUS_DRAFT);
|
resource.setStatus(FirstAidConstants.STATUS_DRAFT);
|
||||||
resource.setAuditStatus(null);
|
resource.setAuditStatus(null);
|
||||||
resource.setAuditRejectReason(null);
|
resource.setAuditRejectReason(null);
|
||||||
|
validateResource(resource);
|
||||||
updateById(resource);
|
updateById(resource);
|
||||||
// 保存编辑后的新内容为当前版本
|
// 保存编辑后的新内容为当前版本
|
||||||
versionService.saveVersion(resource.getId(), resource.getTitle(), resource.getContent(), "编辑更新", true);
|
versionService.saveVersion(resource.getId(), resource.getTitle(), resource.getContent(), "编辑更新", true);
|
||||||
@@ -103,11 +109,16 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
copy.setContent(source.getContent());
|
copy.setContent(source.getContent());
|
||||||
copy.setCoverImage(source.getCoverImage());
|
copy.setCoverImage(source.getCoverImage());
|
||||||
copy.setFileUrl(source.getFileUrl());
|
copy.setFileUrl(source.getFileUrl());
|
||||||
|
copy.setEffectiveTime(source.getEffectiveTime());
|
||||||
|
copy.setExpiryTime(source.getExpiryTime());
|
||||||
copy.setStatus(FirstAidConstants.STATUS_DRAFT);
|
copy.setStatus(FirstAidConstants.STATUS_DRAFT);
|
||||||
copy.setAuditStatus(null);
|
copy.setAuditStatus(null);
|
||||||
copy.setViewCount(0);
|
copy.setViewCount(0);
|
||||||
copy.setFavoriteCount(0);
|
copy.setFavoriteCount(0);
|
||||||
|
validateResource(copy);
|
||||||
save(copy);
|
save(copy);
|
||||||
|
// 保存初始版本
|
||||||
|
versionService.saveVersion(copy.getId(), copy.getTitle(), copy.getContent(), "复制自「" + source.getTitle() + "」", true);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +129,12 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
throw new RuntimeException("资源不存在");
|
throw new RuntimeException("资源不存在");
|
||||||
}
|
}
|
||||||
|
// 仅草稿或已驳回状态可提交审核
|
||||||
|
if (!FirstAidConstants.STATUS_DRAFT.equals(resource.getStatus())) {
|
||||||
|
if (!FirstAidConstants.AUDIT_STATUS_REJECTED.equals(resource.getAuditStatus())) {
|
||||||
|
throw new RuntimeException("当前状态不可提交审核");
|
||||||
|
}
|
||||||
|
}
|
||||||
resource.setAuditStatus(FirstAidConstants.AUDIT_STATUS_PENDING);
|
resource.setAuditStatus(FirstAidConstants.AUDIT_STATUS_PENDING);
|
||||||
resource.setAuditRejectReason(null);
|
resource.setAuditRejectReason(null);
|
||||||
updateById(resource);
|
updateById(resource);
|
||||||
@@ -130,6 +147,9 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
throw new RuntimeException("资源不存在");
|
throw new RuntimeException("资源不存在");
|
||||||
}
|
}
|
||||||
|
if (!FirstAidConstants.STATUS_PUBLISHED.equals(resource.getStatus())) {
|
||||||
|
throw new RuntimeException("仅已发布状态的资源可下架");
|
||||||
|
}
|
||||||
resource.setStatus(FirstAidConstants.STATUS_DOWN);
|
resource.setStatus(FirstAidConstants.STATUS_DOWN);
|
||||||
updateById(resource);
|
updateById(resource);
|
||||||
}
|
}
|
||||||
@@ -157,8 +177,11 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
}
|
}
|
||||||
resource.setAuditStatus(FirstAidConstants.AUDIT_STATUS_APPROVED);
|
resource.setAuditStatus(FirstAidConstants.AUDIT_STATUS_APPROVED);
|
||||||
resource.setAuditRejectReason(null);
|
resource.setAuditRejectReason(null);
|
||||||
resource.setAuditBy(GlobalUtils.getLoginUser().getRealname());
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
resource.setAuditById(GlobalUtils.getLoginUser().getId());
|
if (loginUser != null) {
|
||||||
|
resource.setAuditBy(loginUser.getRealname());
|
||||||
|
resource.setAuditById(loginUser.getId());
|
||||||
|
}
|
||||||
resource.setAuditTime(new Date());
|
resource.setAuditTime(new Date());
|
||||||
updateById(resource);
|
updateById(resource);
|
||||||
// 记录审核日志
|
// 记录审核日志
|
||||||
@@ -174,8 +197,11 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
}
|
}
|
||||||
resource.setAuditStatus(FirstAidConstants.AUDIT_STATUS_REJECTED);
|
resource.setAuditStatus(FirstAidConstants.AUDIT_STATUS_REJECTED);
|
||||||
resource.setAuditRejectReason(rejectReason);
|
resource.setAuditRejectReason(rejectReason);
|
||||||
resource.setAuditBy(GlobalUtils.getLoginUser().getRealname());
|
LoginUser loginUser = GlobalUtils.getLoginUser();
|
||||||
resource.setAuditById(GlobalUtils.getLoginUser().getId());
|
if (loginUser != null) {
|
||||||
|
resource.setAuditBy(loginUser.getRealname());
|
||||||
|
resource.setAuditById(loginUser.getId());
|
||||||
|
}
|
||||||
resource.setAuditTime(new Date());
|
resource.setAuditTime(new Date());
|
||||||
updateById(resource);
|
updateById(resource);
|
||||||
// 记录审核日志
|
// 记录审核日志
|
||||||
@@ -185,14 +211,18 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void batchPublish(List<String> ids) {
|
public void batchPublish(List<String> ids) {
|
||||||
|
// 前置校验:全部审核通过才允许批量发布
|
||||||
for (String id : ids) {
|
for (String id : ids) {
|
||||||
FirstAidResource resource = getById(id);
|
FirstAidResource resource = getById(id);
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
continue;
|
throw new RuntimeException("资源[" + id + "]不存在");
|
||||||
}
|
}
|
||||||
if (!FirstAidConstants.AUDIT_STATUS_APPROVED.equals(resource.getAuditStatus())) {
|
if (!FirstAidConstants.AUDIT_STATUS_APPROVED.equals(resource.getAuditStatus())) {
|
||||||
continue;
|
throw new RuntimeException("资源[" + resource.getTitle() + "]尚未通过审核,无法发布");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
for (String id : ids) {
|
||||||
|
FirstAidResource resource = getById(id);
|
||||||
resource.setStatus(FirstAidConstants.STATUS_PUBLISHED);
|
resource.setStatus(FirstAidConstants.STATUS_PUBLISHED);
|
||||||
updateById(resource);
|
updateById(resource);
|
||||||
}
|
}
|
||||||
@@ -201,12 +231,51 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void batchTakeDown(List<String> ids) {
|
public void batchTakeDown(List<String> ids) {
|
||||||
|
// 前置校验:全部已发布才允许批量下架
|
||||||
for (String id : ids) {
|
for (String id : ids) {
|
||||||
FirstAidResource resource = getById(id);
|
FirstAidResource resource = getById(id);
|
||||||
if (resource != null) {
|
if (resource == null) {
|
||||||
resource.setStatus(FirstAidConstants.STATUS_DOWN);
|
throw new RuntimeException("资源[" + id + "]不存在");
|
||||||
updateById(resource);
|
|
||||||
}
|
}
|
||||||
|
if (!FirstAidConstants.STATUS_PUBLISHED.equals(resource.getStatus())) {
|
||||||
|
throw new RuntimeException("资源[" + resource.getTitle() + "]非已发布状态,无法下架");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String id : ids) {
|
||||||
|
FirstAidResource resource = getById(id);
|
||||||
|
resource.setStatus(FirstAidConstants.STATUS_DOWN);
|
||||||
|
updateById(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteResource(String id) {
|
||||||
|
FirstAidResource resource = getById(id);
|
||||||
|
if (resource == null) {
|
||||||
|
throw new RuntimeException("资源不存在");
|
||||||
|
}
|
||||||
|
if (FirstAidConstants.STATUS_PUBLISHED.equals(resource.getStatus())) {
|
||||||
|
throw new RuntimeException("已发布的资源不可删除,请先下架");
|
||||||
|
}
|
||||||
|
removeById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void batchDeleteResources(List<String> ids) {
|
||||||
|
// 前置校验:全部非已发布才允许删除
|
||||||
|
for (String id : ids) {
|
||||||
|
FirstAidResource resource = getById(id);
|
||||||
|
if (resource == null) {
|
||||||
|
throw new RuntimeException("资源[" + id + "]不存在");
|
||||||
|
}
|
||||||
|
if (FirstAidConstants.STATUS_PUBLISHED.equals(resource.getStatus())) {
|
||||||
|
throw new RuntimeException("资源[" + resource.getTitle() + "]已发布,请先下架再删除");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String id : ids) {
|
||||||
|
removeById(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,12 +309,12 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
queryWrapper.eq(FirstAidResource::getAuditStatus, resource.getAuditStatus());
|
queryWrapper.eq(FirstAidResource::getAuditStatus, resource.getAuditStatus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 标签过滤
|
// 标签过滤(边界精确匹配,避免 id=2 误匹配 id=12/22)
|
||||||
if (StrUtil.isNotBlank(resource.getTags())) {
|
if (StrUtil.isNotBlank(resource.getTags())) {
|
||||||
String[] tagIds = resource.getTags().split(",");
|
String[] tagIds = resource.getTags().split(",");
|
||||||
for (String tagId : tagIds) {
|
for (String tagId : tagIds) {
|
||||||
if (StrUtil.isNotBlank(tagId)) {
|
if (StrUtil.isNotBlank(tagId)) {
|
||||||
queryWrapper.like(FirstAidResource::getTags, tagId.trim());
|
queryWrapper.apply("CONCAT(',', TAGS, ',') LIKE CONCAT('%,', {0}, ',%')", tagId.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,22 +371,39 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
? userActionService.getFavoritedResourceIds(userId, resourceIds)
|
? userActionService.getFavoritedResourceIds(userId, resourceIds)
|
||||||
: Collections.emptyList();
|
: Collections.emptyList();
|
||||||
|
|
||||||
|
// 批量收集所有分类ID和标签ID
|
||||||
|
List<String> allCategoryIds = new ArrayList<>();
|
||||||
for (FirstAidResource resource : resources) {
|
for (FirstAidResource resource : resources) {
|
||||||
if (StrUtil.isNotBlank(resource.getCategoryId())) {
|
if (StrUtil.isNotBlank(resource.getCategoryId())) {
|
||||||
FirstAidResourceCategory category = categoryMapper.selectById(resource.getCategoryId());
|
allCategoryIds.add(resource.getCategoryId());
|
||||||
if (category != null) {
|
}
|
||||||
resource.setCategoryName(category.getName());
|
if (StrUtil.isNotBlank(resource.getTags())) {
|
||||||
|
for (String tagId : resource.getTags().split(",")) {
|
||||||
|
String trimmed = tagId.trim();
|
||||||
|
if (StrUtil.isNotBlank(trimmed)) {
|
||||||
|
allCategoryIds.add(trimmed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// 批量查询,避免 N+1
|
||||||
|
java.util.Map<String, String> nameMap = CollectionUtil.isEmpty(allCategoryIds)
|
||||||
|
? Collections.emptyMap()
|
||||||
|
: categoryMapper.selectBatchIds(allCategoryIds).stream()
|
||||||
|
.collect(Collectors.toMap(FirstAidResourceCategory::getId, FirstAidResourceCategory::getName, (a, b) -> a));
|
||||||
|
|
||||||
|
for (FirstAidResource resource : resources) {
|
||||||
|
if (StrUtil.isNotBlank(resource.getCategoryId())) {
|
||||||
|
resource.setCategoryName(nameMap.get(resource.getCategoryId()));
|
||||||
|
}
|
||||||
if (StrUtil.isNotBlank(resource.getTags())) {
|
if (StrUtil.isNotBlank(resource.getTags())) {
|
||||||
List<String> tagNames = new ArrayList<>();
|
List<String> tagNames = new ArrayList<>();
|
||||||
String[] tagIds = resource.getTags().split(",");
|
for (String tagId : resource.getTags().split(",")) {
|
||||||
for (String tagId : tagIds) {
|
String trimmed = tagId.trim();
|
||||||
String trimmedId = tagId.trim();
|
if (StrUtil.isNotBlank(trimmed)) {
|
||||||
if (StrUtil.isNotBlank(trimmedId)) {
|
String tagName = nameMap.get(trimmed);
|
||||||
FirstAidResourceCategory tag = categoryMapper.selectById(trimmedId);
|
if (tagName != null) {
|
||||||
if (tag != null) {
|
tagNames.add(tagName);
|
||||||
tagNames.add(tag.getName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,6 +413,53 @@ public class FirstAidResourceServiceImpl extends ServiceImpl<FirstAidResourceMap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Set<String> VALID_CONTENT_TYPES = new HashSet<>(Arrays.asList(
|
||||||
|
FirstAidConstants.CONTENT_TYPE_IMAGE_TEXT,
|
||||||
|
FirstAidConstants.CONTENT_TYPE_VIDEO,
|
||||||
|
FirstAidConstants.CONTENT_TYPE_AUDIO,
|
||||||
|
FirstAidConstants.CONTENT_TYPE_PDF
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验资源的分类和标签引用合法性
|
||||||
|
*/
|
||||||
|
private void validateResource(FirstAidResource resource) {
|
||||||
|
// 校验内容类型
|
||||||
|
if (StrUtil.isNotBlank(resource.getContentType())
|
||||||
|
&& !VALID_CONTENT_TYPES.contains(resource.getContentType())) {
|
||||||
|
throw new RuntimeException("无效的内容类型: " + resource.getContentType());
|
||||||
|
}
|
||||||
|
// 校验分类存在且为分类类型
|
||||||
|
if (StrUtil.isNotBlank(resource.getCategoryId())) {
|
||||||
|
FirstAidResourceCategory category = categoryMapper.selectById(resource.getCategoryId());
|
||||||
|
if (category == null || !FirstAidConstants.TYPE_CATEGORY.equals(category.getType())) {
|
||||||
|
throw new RuntimeException("所选分类不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 校验标签存在且为标签类型
|
||||||
|
if (StrUtil.isNotBlank(resource.getTags())) {
|
||||||
|
String[] parts = resource.getTags().split(",");
|
||||||
|
Set<String> tagIdSet = new HashSet<>();
|
||||||
|
for (String p : parts) {
|
||||||
|
String trimmed = p.trim();
|
||||||
|
if (StrUtil.isNotBlank(trimmed)) {
|
||||||
|
tagIdSet.add(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (CollectionUtil.isNotEmpty(tagIdSet)) {
|
||||||
|
List<FirstAidResourceCategory> tags = categoryMapper.selectBatchIds(new ArrayList<>(tagIdSet));
|
||||||
|
if (tags.size() != tagIdSet.size()) {
|
||||||
|
throw new RuntimeException("所选标签不存在");
|
||||||
|
}
|
||||||
|
for (FirstAidResourceCategory tag : tags) {
|
||||||
|
if (!FirstAidConstants.TYPE_TAG.equals(tag.getType())) {
|
||||||
|
throw new RuntimeException("标签[" + tag.getName() + "]类型不正确");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算导出用的状态文本(审核状态优先于发布状态)
|
* 计算导出用的状态文本(审核状态优先于发布状态)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+19
-31
@@ -37,23 +37,15 @@ public class FirstAidResourceUserActionServiceImpl extends ServiceImpl<FirstAidR
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean recordRead(String resourceId, String userId) {
|
public boolean recordRead(String resourceId, String userId) {
|
||||||
// 检查是否已有阅读记录
|
|
||||||
LambdaQueryWrapper<FirstAidResourceUserAction> queryWrapper = new LambdaQueryWrapper<>();
|
|
||||||
queryWrapper.eq(FirstAidResourceUserAction::getResourceId, resourceId)
|
|
||||||
.eq(FirstAidResourceUserAction::getUserId, userId)
|
|
||||||
.eq(FirstAidResourceUserAction::getType, FirstAidConstants.ACTION_TYPE_READ);
|
|
||||||
if (ObjectUtil.isNotNull(getOne(queryWrapper, false))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 插入阅读记录
|
|
||||||
FirstAidResourceUserAction action = new FirstAidResourceUserAction();
|
FirstAidResourceUserAction action = new FirstAidResourceUserAction();
|
||||||
action.setResourceId(resourceId);
|
action.setResourceId(resourceId);
|
||||||
action.setUserId(userId);
|
action.setUserId(userId);
|
||||||
action.setType(FirstAidConstants.ACTION_TYPE_READ);
|
action.setType(FirstAidConstants.ACTION_TYPE_READ);
|
||||||
save(action);
|
try {
|
||||||
|
save(action);
|
||||||
// 浏览计数+1
|
} catch (org.springframework.dao.DuplicateKeyException e) {
|
||||||
|
return false; // 已有阅读记录
|
||||||
|
}
|
||||||
resourceMapper.incrementViewCount(resourceId);
|
resourceMapper.incrementViewCount(resourceId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -61,27 +53,23 @@ public class FirstAidResourceUserActionServiceImpl extends ServiceImpl<FirstAidR
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean toggleFavorite(String resourceId, String userId) {
|
public boolean toggleFavorite(String resourceId, String userId) {
|
||||||
// 查是否已收藏
|
FirstAidResourceUserAction action = new FirstAidResourceUserAction();
|
||||||
LambdaQueryWrapper<FirstAidResourceUserAction> queryWrapper = new LambdaQueryWrapper<>();
|
action.setResourceId(resourceId);
|
||||||
queryWrapper.eq(FirstAidResourceUserAction::getResourceId, resourceId)
|
action.setUserId(userId);
|
||||||
.eq(FirstAidResourceUserAction::getUserId, userId)
|
action.setType(FirstAidConstants.ACTION_TYPE_FAVORITE);
|
||||||
.eq(FirstAidResourceUserAction::getType, FirstAidConstants.ACTION_TYPE_FAVORITE);
|
try {
|
||||||
FirstAidResourceUserAction existing = getOne(queryWrapper, false);
|
|
||||||
|
|
||||||
if (ObjectUtil.isNotNull(existing)) {
|
|
||||||
// 已收藏 → 取消收藏
|
|
||||||
removeById(existing.getId());
|
|
||||||
resourceMapper.decrementFavoriteCount(resourceId);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// 未收藏 → 收藏
|
|
||||||
FirstAidResourceUserAction action = new FirstAidResourceUserAction();
|
|
||||||
action.setResourceId(resourceId);
|
|
||||||
action.setUserId(userId);
|
|
||||||
action.setType(FirstAidConstants.ACTION_TYPE_FAVORITE);
|
|
||||||
save(action);
|
save(action);
|
||||||
resourceMapper.incrementFavoriteCount(resourceId);
|
resourceMapper.incrementFavoriteCount(resourceId);
|
||||||
return true;
|
return true;
|
||||||
|
} catch (org.springframework.dao.DuplicateKeyException e) {
|
||||||
|
// 已收藏 → 取消收藏
|
||||||
|
LambdaQueryWrapper<FirstAidResourceUserAction> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(FirstAidResourceUserAction::getResourceId, resourceId)
|
||||||
|
.eq(FirstAidResourceUserAction::getUserId, userId)
|
||||||
|
.eq(FirstAidResourceUserAction::getType, FirstAidConstants.ACTION_TYPE_FAVORITE);
|
||||||
|
remove(queryWrapper);
|
||||||
|
resourceMapper.decrementFavoriteCount(resourceId);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
@@ -1,5 +1,6 @@
|
|||||||
package com.renkang.emergency.service.impl;
|
package com.renkang.emergency.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.renkang.emergency.constant.FirstAidConstants;
|
import com.renkang.emergency.constant.FirstAidConstants;
|
||||||
@@ -23,6 +24,9 @@ import java.util.List;
|
|||||||
@Service
|
@Service
|
||||||
public class FirstAidResourceVersionServiceImpl extends ServiceImpl<FirstAidResourceVersionMapper, FirstAidResourceVersion> implements IFirstAidResourceVersionService {
|
public class FirstAidResourceVersionServiceImpl extends ServiceImpl<FirstAidResourceVersionMapper, FirstAidResourceVersion> implements IFirstAidResourceVersionService {
|
||||||
|
|
||||||
|
/** 每个资源最多保留的版本数(含当前版本) */
|
||||||
|
private static final int MAX_VERSIONS = 20;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FirstAidResourceVersionMapper versionMapper;
|
private FirstAidResourceVersionMapper versionMapper;
|
||||||
|
|
||||||
@@ -52,6 +56,24 @@ public class FirstAidResourceVersionServiceImpl extends ServiceImpl<FirstAidReso
|
|||||||
version.setChangeDescription(changeDescription);
|
version.setChangeDescription(changeDescription);
|
||||||
version.setIsCurrent(isCurrent);
|
version.setIsCurrent(isCurrent);
|
||||||
save(version);
|
save(version);
|
||||||
|
|
||||||
|
// 清理超出上限的旧版本(当前版本 + 最近 MAX_VERSIONS-1 条非当前版本,其余删除)
|
||||||
|
cleanOldVersions(resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理超出保留上限的旧版本,保留当前版本 + 最近N条非当前版本
|
||||||
|
*/
|
||||||
|
private void cleanOldVersions(String resourceId) {
|
||||||
|
List<String> nonCurrentIds = versionMapper.selectNonCurrentVersionIds(resourceId);
|
||||||
|
if (nonCurrentIds.size() <= MAX_VERSIONS - 1) {
|
||||||
|
return; // 未超上限,无需清理
|
||||||
|
}
|
||||||
|
// 保留最近 MAX_VERSIONS-1 条非当前版本,删除其余
|
||||||
|
List<String> toDelete = nonCurrentIds.subList(MAX_VERSIONS - 1, nonCurrentIds.size());
|
||||||
|
if (CollUtil.isNotEmpty(toDelete)) {
|
||||||
|
removeByIds(toDelete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user