瀏覽代碼

feat(portal): 新增新闻管理功能及文件上传优化

- 新增新闻增删改查相关接口和数据传输对象
- 实现通用文件上传功能,支持文件覆盖和OSS存储
- 添加新闻不存在和文件上传失败等异常错误码- 完善设备参数校验规则,增加非空验证-优化角色菜单查询逻辑,支持多角色ID查询
- 用户上下文增加角色ID获取方法- 移除重复的文件名生成和OTA文件校验逻辑,统一调用服务层实现
hxd 13 小時之前
父節點
當前提交
542b19999d
共有 20 個文件被更改,包括 603 次插入68 次删除
  1. 37 0
      portal-service-application/src/main/java/com/hfln/portal/application/controller/web/WebSystemController.java
  2. 44 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/news/NewsDTO.java
  3. 7 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/device/UpdateDeviceParams.java
  4. 7 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/device/WebUpdateDeviceParams.java
  5. 15 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/DeleteNewsParam.java
  6. 50 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/EditNewsParam.java
  7. 21 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/QueryNewsListParam.java
  8. 6 0
      portal-service-domain/src/main/java/com/hfln/portal/domain/exception/ErrorEnum.java
  9. 19 1
      portal-service-domain/src/main/java/com/hfln/portal/domain/gateway/WebGateway.java
  10. 14 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/config/UserContext.java
  11. 2 20
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/UserGatewayImpl.java
  12. 92 47
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebGatewayImpl.java
  13. 13 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/TblIndustryNewsMapper.java
  14. 71 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/TblIndustryNews.java
  15. 7 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/OssFileService.java
  16. 12 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/TblIndustryNewsService.java
  17. 2 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/TblRoleMenuMapService.java
  18. 128 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/OssFileServiceImpl.java
  19. 43 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/TblIndustryNewsServiceImpl.java
  20. 13 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/TblRoleMenuMapServiceImpl.java

+ 37 - 0
portal-service-application/src/main/java/com/hfln/portal/application/controller/web/WebSystemController.java

@@ -5,6 +5,8 @@ import cn.hfln.framework.catchlog.CatchAndLog;
 import cn.hfln.framework.dto.ApiResult;
 import com.hfln.portal.common.dto.data.menu.MenuListDTO;
 import com.hfln.portal.common.dto.data.menu.MenuTreeDTO;
+import com.hfln.portal.common.dto.data.news.NewsDTO;
+import com.hfln.portal.common.dto.data.oss.OssFileDTO;
 import com.hfln.portal.common.dto.data.parameter.ParameterDTO;
 import com.hfln.portal.common.dto.data.rolemenu.RoleMenuTreeDTO;
 import com.hfln.portal.common.request.web.*;
@@ -99,4 +101,39 @@ public class WebSystemController {
     public ApiResult<List<ParameterDTO>> queryParameter(@RequestBody @Valid ParameterQueryParam param) {
         return ApiResult.success(webGateway.queryParameter(param));
     }
+
+    @PostMapping("/editNews")
+    @Operation(summary = "添加或修改新闻")
+    public ApiResult<Void> editNews(@Valid @ModelAttribute EditNewsParam param) {
+        webGateway.editNews(param);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/deleteNews")
+    @Operation(summary = "删除新闻")
+    public ApiResult<Void> deleteNews(@RequestBody @Valid DeleteNewsParam param) {
+        webGateway.deleteNews(param);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/queryNewsList")
+    @Operation(summary = "查询新闻列表")
+    public ApiResult<List<NewsDTO>> queryNewsList(@RequestBody @Valid QueryNewsListParam param) {
+        return ApiResult.success(webGateway.queryNewsList(param));
+    }
+
+    @GetMapping("/queryNewDetails")
+    @Operation(summary = "查询新闻详情")
+    public ApiResult<NewsDTO> queryNewDetails(@RequestParam Long newsId) {
+        return ApiResult.success(webGateway.queryNewDetails(newsId));
+    }
+
+    @PostMapping(value = "/uploadFile")
+    @Operation(summary = "上传文件", description = "上传通用文件,文件类型根据字典接口查询 oss_busi_type 然后下拉选择 ")
+    public ApiResult<OssFileDTO> uploadCommonFile(@Valid @RequestParam MultipartFile file,
+                                                  @RequestParam String fileType,
+                                                  @RequestParam String busiKey)  {
+        webGateway.uploadCommonFile(file, fileType, busiKey);
+        return ApiResult.success();
+    }
 }

+ 44 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/news/NewsDTO.java

@@ -0,0 +1,44 @@
+package com.hfln.portal.common.dto.data.news;
+
+import com.hfln.portal.common.vo.BaseDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class NewsDTO extends BaseDto {
+
+    @Schema(description = "新闻id")
+    private Long newsId;
+
+    @Schema(description = "新闻标题")
+    private String newsTitle;
+
+    @Schema(description = "新闻类型")
+    private String newsType;
+
+    @Schema(description = "新闻内容")
+    private String newsSummary;
+
+    @Schema(description = "新闻来源")
+    private String newsSource;
+
+    @Schema(description = "新闻相关性")
+    private String newsRelevance;
+
+    @Schema(description = "新闻热度")
+    private String newsPopularity;
+
+    @Schema(description = "发布时间")
+    private LocalDateTime publishTime;
+
+    @Schema(description = "新闻图片")
+    private String newsImageUrl;
+
+    @Schema(description = "外链地址")
+    private String newsLink;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 7 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/device/UpdateDeviceParams.java

@@ -30,6 +30,7 @@ public class UpdateDeviceParams extends BaseVO {
     @Schema(description = "安装高度: 200 - 370 cm 之内")
 //    @DecimalMax(value = "370", message = "height不能大于370")
 //    @DecimalMin(value = "200", message = "height不能小于200")
+    @NotNull(message = "安装高度不能为空")
     private BigDecimal height;
 
 
@@ -47,34 +48,40 @@ public class UpdateDeviceParams extends BaseVO {
     @Schema(description = "雷达监测范围x开始,范围:-200-200 cm之内")
 //    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
 //    @DecimalMax(value = "200", message = "xxStart不能大于200")
+    @NotNull(message = "雷达监测范围x开始不能为空")
     private BigDecimal xxStart;
 
     @Schema(description = "雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart")
 //    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
 //    @DecimalMax(value = "200", message = "xxEnd不能大于200")
+    @NotNull(message = "雷达监测范围x结束不能为空")
     private BigDecimal xxEnd;
 
 
     @Schema(description = "雷达监测范围y开始,范围:-250-250 cm之内")
 //    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
 //    @DecimalMax(value = "250", message = "yyStart不能大于250")
+    @NotNull(message = "雷达监测范围y开始不能为空")
     private BigDecimal yyStart;
 
     @Schema(description = "雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart")
 //    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
 //    @DecimalMax(value = "250", message = "yyEnd不能大于250")
+    @NotNull(message = "雷达监测范围y结束不能为空")
     private BigDecimal yyEnd;
 
 
     @Schema(description = "雷达监测范围z开始,范围:0-5 cm之内")
 //    @DecimalMin(value = "0", message = "zzStart不能小于0")
 //    @DecimalMax(value = "5", message = "zzStart不能大于5")
+    @NotNull(message = "雷达监测范围z开始不能为空")
     private BigDecimal zzStart;
 
 
     @Schema(description = "雷达监测范围z结束,范围:200-300 cm之内")
 //    @DecimalMin(value = "200", message = "zzEnd不能小于200")
 //    @DecimalMax(value = "300", message = "zzEnd不能大于300")
+    @NotNull(message = "雷达监测范围z结束不能为空")
     private BigDecimal zzEnd;
 
 

+ 7 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/device/WebUpdateDeviceParams.java

@@ -32,6 +32,7 @@ public class WebUpdateDeviceParams extends BaseVO {
     @Schema(description = "安装高度: 200 - 370 cm 之内")
 //    @DecimalMax(value = "370", message = "height不能大于370")
 //    @DecimalMin(value = "200", message = "height不能小于200")
+    @NotNull(message = "安装高度不能为空")
     private BigDecimal height;
 
     @Schema(description = "WIFI名称")
@@ -54,34 +55,40 @@ public class WebUpdateDeviceParams extends BaseVO {
     @Schema(description = "雷达监测范围x开始,范围:-200-200 cm之内")
 //    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
 //    @DecimalMax(value = "200", message = "xxStart不能大于200")
+    @NotNull(message = "雷达监测范围x开始不能为空")
     private BigDecimal xxStart;
 
     @Schema(description = "雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart")
 //    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
 //    @DecimalMax(value = "200", message = "xxEnd不能大于200")
+    @NotNull(message = "雷达监测范围x结束不能为空")
     private BigDecimal xxEnd;
 
 
     @Schema(description = "雷达监测范围y开始,范围:-250-250 cm之内")
 //    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
 //    @DecimalMax(value = "250", message = "yyStart不能大于250")
+    @NotNull(message = "雷达监测范围y开始不能为空")
     private BigDecimal yyStart;
 
     @Schema(description = "雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart")
 //    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
 //    @DecimalMax(value = "250", message = "yyEnd不能大于250")
+    @NotNull(message = "雷达监测范围y结束不能为空")
     private BigDecimal yyEnd;
 
 
     @Schema(description = "雷达监测范围z开始,范围:0-5 cm之内")
 //    @DecimalMin(value = "0", message = "zzStart不能小于0")
 //    @DecimalMax(value = "5", message = "zzStart不能大于5")
+    @NotNull(message = "雷达监测范围z开始不能为空")
     private BigDecimal zzStart;
 
 
     @Schema(description = "雷达监测范围z结束,范围:200-300 cm之内")
 //    @DecimalMin(value = "200", message = "zzEnd不能小于200")
 //    @DecimalMax(value = "300", message = "zzEnd不能大于300")
+    @NotNull(message = "雷达监测范围z结束不能为空")
     private BigDecimal zzEnd;
 
     @Schema(description = "设备跌倒确认时间,大于0")

+ 15 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/DeleteNewsParam.java

@@ -0,0 +1,15 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class DeleteNewsParam extends BaseVO {
+
+    @Schema(description = "新闻ID")
+    @NotNull(message = "新闻ID不能为空")
+    private Long newsId;
+}

+ 50 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/EditNewsParam.java

@@ -0,0 +1,50 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotBlank;
+import java.time.LocalDateTime;
+
+
+@Data
+public class EditNewsParam extends BaseVO {
+
+    @Schema(description = "新闻id")
+    private Long newsId;
+
+    @Schema(description = "新闻标题")
+    @NotBlank(message = "新闻标题不能为空")
+    private String newsTitle;
+
+    @Schema(description = "新闻类型")
+    @NotBlank(message = "新闻类型不能为空")
+    private String newsType;
+
+    @Schema(description = "新闻内容")
+    @NotBlank(message = "新闻内容不能为空")
+    private String newsSummary;
+
+    @Schema(description = "新闻来源")
+    private String newsSource;
+
+    @Schema(description = "新闻相关性")
+    private String newsRelevance;
+
+    @Schema(description = "新闻热度")
+    private String newsPopularity;
+
+    @Schema(description = "发布时间")
+    private LocalDateTime publishTime;
+
+    @Schema(description = "新闻图片")
+    private MultipartFile newsImage;
+
+    @Schema(description = "外链地址")
+    private String newsLink;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 21 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/QueryNewsListParam.java

@@ -0,0 +1,21 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+
+@Data
+public class QueryNewsListParam extends BaseVO {
+
+    @Schema(description = "新闻标题")
+    private String newsTitle;
+
+    @Schema(description = "新闻类型")
+    private String newsType;
+
+    @Schema(description = "发布时间")
+    private LocalDate publishTime;
+}

+ 6 - 0
portal-service-domain/src/main/java/com/hfln/portal/domain/exception/ErrorEnum.java

@@ -98,6 +98,7 @@ public enum ErrorEnum implements ErrorEnumInterface {
     FILE_EXIST_MULTIPLE("80006", "存在多个文件!"),
     FILE_TYPE_IS_NULL("80007", "文件类型不能为空!"),
     FILE_ONLY_BIN_HEX_FW("80008", "文件格式仅支持 BIN/HEX/FW!"),
+    FILE_UPLOAD_FAIL("80009", "文件上传失败!"),
 
 
     /**
@@ -163,6 +164,11 @@ public enum ErrorEnum implements ErrorEnumInterface {
      */
     PARAM_CODE_EXISTS("17001", "系统参数编码已存在!"),
     PARAM_NOT_EXIST("17002", "系统参数不存在!"),
+
+    /**
+     * 新闻相关
+     */
+    NEWS_NOT_EXIST("18001", "新闻不存在!"),
     ;
 
     private final String errorCode;

+ 19 - 1
portal-service-domain/src/main/java/com/hfln/portal/domain/gateway/WebGateway.java

@@ -4,6 +4,9 @@ import com.hfln.portal.common.dto.data.device.DeviceDTO;
 import com.hfln.portal.common.dto.data.dic.DicDTO;
 import com.hfln.portal.common.dto.data.menu.MenuListDTO;
 import com.hfln.portal.common.dto.data.menu.MenuTreeDTO;
+import com.hfln.portal.common.dto.data.news.NewsDTO;
+import com.hfln.portal.common.dto.data.oss.OssFileDTO;
+import com.hfln.portal.common.dto.data.parameter.ParameterDTO;
 import com.hfln.portal.common.dto.data.role.RoleListDTO;
 import com.hfln.portal.common.dto.data.rolemenu.RoleMenuTreeDTO;
 import com.hfln.portal.common.request.device.DeviceListQueryReq;
@@ -11,7 +14,6 @@ import com.hfln.portal.common.request.web.*;
 import com.hfln.portal.common.response.admin.AdminLoginRes;
 import com.hfln.portal.common.vo.PageRecord;
 import com.hfln.portal.common.vo.UploadRes;
-import com.hfln.portal.common.dto.data.parameter.ParameterDTO;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
@@ -99,5 +101,21 @@ public interface WebGateway {
     void deleteParameter(ParameterDeleteParam Param);
 
     List<ParameterDTO> queryParameter(ParameterQueryParam param);
+
+    /**
+     * web 新闻相关
+     */
+    void editNews(EditNewsParam param);
+
+    void deleteNews(DeleteNewsParam  param);
+
+    List<NewsDTO> queryNewsList(QueryNewsListParam param);
+
+    NewsDTO queryNewDetails(Long newsId);
+
+    /**
+     * web 上传通用文件
+     */
+    OssFileDTO uploadCommonFile(MultipartFile file, String fileType, String busiKey) ;
 }
 

+ 14 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/config/UserContext.java

@@ -1,5 +1,7 @@
 package com.hfln.portal.infrastructure.config;
 
+import java.util.List;
+
 public class UserContext {
     private static final ThreadLocal<LoginUser> loginUserHolder = new ThreadLocal<>();
 
@@ -65,6 +67,11 @@ public class UserContext {
         return loginUser == null ? null : loginUser.getTenantName();
     }
 
+    public static List<Long> getRoleIds() {
+        LoginUser loginUser = loginUserHolder.get();
+        return loginUser == null ? null : loginUser.getRoleIds();
+    }
+
     public static void setTenantName(String tenantName) {
         LoginUser loginUser = loginUserHolder.get();
         if (loginUser != null) {
@@ -135,4 +142,11 @@ public class UserContext {
         }
     }
 
+    public static void setRoleIds(List<Long> roleIds) {
+        LoginUser loginUser = loginUserHolder.get();
+        if (loginUser != null) {
+            loginUser.setRoleIds(roleIds);
+        }
+    }
+
 }

+ 2 - 20
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/UserGatewayImpl.java

@@ -50,8 +50,6 @@ import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -486,7 +484,7 @@ public class UserGatewayImpl implements UserGateway {
         }
 
         // 生成OSS对象名称,使用原始文件名
-        String objectName = generateCommonObjectName(fileType, originalFilename);
+        String objectName = ossFileService.generateCommonObjectName(fileType, originalFilename);
 
         // 上传文件到OSS
         ossClient.upload(file.getInputStream(), OssUtils.BUCKET_NAME, objectName);
@@ -532,21 +530,5 @@ public class UserGatewayImpl implements UserGateway {
         }
     }
 
-    /**
-     * 生成通用文件的OSS对象名称,保留原始文件名
-     *
-     * @param fileType         文件类型
-     * @param originalFilename 原始文件名
-     * @return OSS对象名称
-     */
-    private String generateCommonObjectName(String fileType, String originalFilename) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(fileType)
-                .append("/")
-                .append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
-                .append("/")
-                .append(originalFilename); // 直接使用原始文件名
-
-        return sb.toString();
-    }
+
 }

+ 92 - 47
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebGatewayImpl.java

@@ -15,8 +15,10 @@ import com.hfln.portal.common.dto.data.device.DeviceDTO;
 import com.hfln.portal.common.dto.data.dic.DicDTO;
 import com.hfln.portal.common.dto.data.menu.MenuListDTO;
 import com.hfln.portal.common.dto.data.menu.MenuTreeDTO;
-import com.hfln.portal.common.dto.data.role.RoleListDTO;
+import com.hfln.portal.common.dto.data.news.NewsDTO;
+import com.hfln.portal.common.dto.data.oss.OssFileDTO;
 import com.hfln.portal.common.dto.data.parameter.ParameterDTO;
+import com.hfln.portal.common.dto.data.role.RoleListDTO;
 import com.hfln.portal.common.dto.data.rolemenu.MenuSimpleTreeDTO;
 import com.hfln.portal.common.dto.data.rolemenu.RoleMenuTreeDTO;
 import com.hfln.portal.common.request.device.DeviceListQueryReq;
@@ -50,7 +52,6 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -61,6 +62,9 @@ public class WebGatewayImpl implements WebGateway {
     @Value("${oss.ota-download-url-pre}")
     private String otaDownloadUrlPre;
 
+    @Value("${oss.download-url-pre}")
+    private String downloadUrlPre;
+
     @Autowired
     private DevInfoService devInfoService;
 
@@ -112,6 +116,11 @@ public class WebGatewayImpl implements WebGateway {
     @Autowired
     private PersonInOutService personInOutService;
 
+    @Autowired
+    private TblIndustryNewsService tblIndustryNewsService;
+
+
+
     @Override
     public void upsertParameter(ParameterUpsertParam param) {
 
@@ -680,15 +689,12 @@ public class WebGatewayImpl implements WebGateway {
         res.setRoleId(roleId);
 
         // 查询角色已选菜单ID
-        //TODO   查询前端传的roleId,显示这个角色所勾选的菜单
         List<Long> selectedMenuId = tblRoleMenuMapService.queryMenuIdByRoleId(roleId);
         res.setSelectedMenuId(selectedMenuId);
 
-        // 查询所有菜单
-        //TODO   查询redis中当前用户的roleId,显示为机构超管的所有菜单
-        List<TblSystemMenu> allMenus = tblSystemMenuService.lambdaQuery()
-                .eq(TblSystemMenu::getIsDeleted, BasePO.DeleteFlag.NOT_DELETED)
-                .list();
+        // 查询机构对应超管所有菜单id-- 从redis中取当前登陆者的roleIds,然后取菜单合集
+        List<Long> roleMenuId = tblRoleMenuMapService.queryMenuIdByRoleIds(UserContext.getRoleIds());
+        List<TblSystemMenu> allMenus = tblSystemMenuService.listByIds(roleMenuId);
 
         // 转换为DTO
         List<MenuSimpleTreeDTO> menuVOList = CopyUtils.copyList(allMenus, MenuSimpleTreeDTO.class);
@@ -796,6 +802,82 @@ public class WebGatewayImpl implements WebGateway {
         tblDicItemService.removeById(tblDicItem);
     }
 
+    @Override
+    public void editNews(EditNewsParam param) {
+
+        if (param.getNewsId() != null) {
+            // 修改新闻逻辑
+            TblIndustryNews tblIndustryNews = tblIndustryNewsService.getById(param.getNewsId());
+            if (tblIndustryNews == null) {
+                throw new BizException(ErrorEnum.NEWS_NOT_EXIST.getErrorCode(), ErrorEnum.NEWS_NOT_EXIST.getErrorMessage());
+            }
+            String fileType = "NEWS";
+            String busiKey = tblIndustryNews.getNewsId().toString();
+            try {
+            TblOssFile ossFile = ossFileService.uploadCommonFile(param.getNewsImage(), fileType, busiKey);
+            BeanUtils.copyProperties(param, tblIndustryNews, "newsImage");
+            tblIndustryNews.setNewsImageUrl(ossFile.getOssUrl());
+            tblIndustryNewsService.updateById(tblIndustryNews);
+            } catch (IOException e) {
+                log.error("上传新闻图片失败", e);
+                throw new BizException(ErrorEnum.FILE_UPLOAD_FAIL.getErrorCode(), ErrorEnum.FILE_UPLOAD_FAIL.getErrorMessage());
+            }
+        } else {
+            // 新增新闻逻辑
+            TblIndustryNews tblIndustryNews = new TblIndustryNews();
+            BeanUtils.copyProperties(param, tblIndustryNews, "newsImage");
+            tblIndustryNewsService.save(tblIndustryNews);
+            Long newsId = tblIndustryNews.getNewsId();
+            String fileType = "NEWS";
+            String busiKey = newsId.toString();
+            try {
+            TblOssFile ossFile = ossFileService.uploadCommonFile(param.getNewsImage(), fileType, busiKey);
+            tblIndustryNews.setNewsImageUrl(ossFile.getOssUrl());
+            tblIndustryNewsService.updateById(tblIndustryNews);
+            } catch (IOException e) {
+                log.error("上传新闻图片失败", e);
+                throw new BizException(ErrorEnum.FILE_UPLOAD_FAIL.getErrorCode(), ErrorEnum.FILE_UPLOAD_FAIL.getErrorMessage());
+            }
+        }
+    }
+
+    @Override
+    public void deleteNews(DeleteNewsParam param) {
+        TblIndustryNews tblIndustryNews = tblIndustryNewsService.getById(param.getNewsId());
+        if (tblIndustryNews == null) {
+            throw new BizException(ErrorEnum.NEWS_NOT_EXIST.getErrorCode(), ErrorEnum.NEWS_NOT_EXIST.getErrorMessage());
+        }
+
+        tblIndustryNews.setIsDeleted(BasePO.DeleteFlag.DELETED);
+        tblIndustryNewsService.removeById(tblIndustryNews);
+    }
+
+    @Override
+    public List<NewsDTO> queryNewsList(QueryNewsListParam param) {
+        List<TblIndustryNews> tblIndustryNewsList = tblIndustryNewsService.queryNewsList(param);
+        return CopyUtils.copyList(tblIndustryNewsList, NewsDTO.class);
+    }
+
+    @Override
+    public NewsDTO queryNewDetails(Long newsId) {
+
+        return CopyUtils.copy(tblIndustryNewsService.getById(newsId), NewsDTO.class);
+
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public OssFileDTO uploadCommonFile(MultipartFile file, String fileType, String busiKey){
+        try {
+            TblOssFile ossFile = ossFileService.uploadCommonFile(file, fileType, busiKey);
+            return CopyUtils.copy(ossFile, OssFileDTO.class);
+        } catch (IOException e) {
+            log.error("上传文件失败", e);
+            throw new BizException(ErrorEnum.FILE_UPLOAD_FAIL.getErrorCode(), ErrorEnum.FILE_UPLOAD_FAIL.getErrorMessage());
+        }
+    }
+
+
 
     @Override
     public void OTAUpload(MultipartFile file) throws IOException {
@@ -806,12 +888,12 @@ public class WebGatewayImpl implements WebGateway {
 
         // 校验文件类型 - OTA升级文件通常是固件文件,支持常见的固件格式
         String originalFilename = file.getOriginalFilename();
-        if (originalFilename == null || !isValidOtaFile(originalFilename)) {
+        if (originalFilename == null || !ossFileService.isValidOtaFile(originalFilename)) {
             throw new BizException(ErrorEnum.FILE_ONLY_BIN_HEX_FW.getErrorCode(), "仅支持固件文件格式(.bin, .hex, .fw等)!");
         }
 
         // 生成OTA专用的OSS对象名称,使用单独的文件夹
-        String objectName = generateOtaObjectName(originalFilename);
+        String objectName = ossFileService.generateCommonObjectName("ota", originalFilename);
 
         // 上传文件到OSS
         ossClient.upload(file.getInputStream(), OssUtils.BUCKET_NAME, objectName);
@@ -829,41 +911,4 @@ public class WebGatewayImpl implements WebGateway {
         log.info("OTA升级文件上传成功:文件名={}, OSS路径={}", originalFilename, objectName);
     }
 
-    /**
-     * 校验是否为有效的OTA文件
-     *
-     * @param fileName 文件名
-     * @return 是否为有效文件
-     */
-    private boolean isValidOtaFile(String fileName) {
-        if (fileName == null) {
-            return false;
-        }
-        String lowerFileName = fileName.toLowerCase();
-        // 支持常见的固件文件格式
-        return lowerFileName.endsWith(".bin") ||
-                lowerFileName.endsWith(".hex") ||
-                lowerFileName.endsWith(".fw") ||
-                lowerFileName.endsWith(".img") ||
-                lowerFileName.endsWith(".tar.gz") ||
-                lowerFileName.endsWith(".zip");
-    }
-
-    /**
-     * 生成OTA专用的OSS对象名称
-     *
-     * @param originalFilename 原始文件名
-     * @return OSS对象名称
-     */
-    private String generateOtaObjectName(String originalFilename) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("ota/") // 使用单独的ota文件夹
-                .append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
-                .append("/")
-                .append(UUID.randomUUID().toString().replace("-", ""))
-                .append("_")
-                .append(originalFilename); // 保留原始文件名以便识别
-
-        return sb.toString();
-    }
 }

+ 13 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/TblIndustryNewsMapper.java

@@ -0,0 +1,13 @@
+package com.hfln.portal.infrastructure.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hfln.portal.infrastructure.po.TblIndustryNews;
+import org.apache.ibatis.annotations.Mapper;
+
+
+/**
+ * 业界新闻表 Mapper接口
+ */
+@Mapper
+public interface TblIndustryNewsMapper extends BaseMapper<TblIndustryNews> {
+}

+ 71 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/TblIndustryNews.java

@@ -0,0 +1,71 @@
+package com.hfln.portal.infrastructure.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * 业界新闻表
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("tbl_industry_news")
+public class TblIndustryNews extends BasePO{
+
+
+    /**
+     * 主键ID,数据库自增策略
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long newsId;
+
+    /**
+     * 新闻标题
+     */
+    private String newsTitle;
+
+    /**
+     * 新闻类型
+     */
+    private String newsType;
+
+    /**
+     * 新闻简内容
+     */
+    private String newsSummary;
+
+    /**
+     * 新闻来源
+     */
+    private String newsSource;
+
+    /**
+     * 新闻相关性
+     */
+    private String newsRelevance;
+
+    /**
+     * 新闻热度
+     */
+    private String newsPopularity;
+
+    /**
+     * 发布时间
+     */
+    private LocalDateTime publishTime;
+
+    /**
+     * 新闻图片
+     */
+    private String newsImageUrl;
+
+    /**
+     * 外链地址
+     */
+    private String newsLink;
+
+}

+ 7 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/OssFileService.java

@@ -2,7 +2,9 @@ package com.hfln.portal.infrastructure.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.hfln.portal.infrastructure.po.TblOssFile;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
 import java.util.List;
 
 public interface OssFileService extends IService<TblOssFile> {
@@ -12,4 +14,9 @@ public interface OssFileService extends IService<TblOssFile> {
 
     TblOssFile queryOneFile(String busiType, String busiKey, String fileName);
 
+    TblOssFile uploadCommonFile(MultipartFile file, String fileType, String busiKey) throws IOException;
+
+    String generateCommonObjectName(String fileType, String originalFilename);
+
+    boolean isValidOtaFile(String fileName);
 }

+ 12 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/TblIndustryNewsService.java

@@ -0,0 +1,12 @@
+package com.hfln.portal.infrastructure.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hfln.portal.common.request.web.QueryNewsListParam;
+import com.hfln.portal.infrastructure.po.TblIndustryNews;
+
+import java.util.List;
+
+public interface TblIndustryNewsService extends IService<TblIndustryNews> {
+
+    List<TblIndustryNews> queryNewsList(QueryNewsListParam param);
+}

+ 2 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/TblRoleMenuMapService.java

@@ -10,4 +10,6 @@ public interface TblRoleMenuMapService extends IService<TblRoleMenuMap> {
     List<TblRoleMenuMap> findByRoleId(Long roleId);
 
     List<Long> queryMenuIdByRoleId(Long roleId);
+
+    List<Long> queryMenuIdByRoleIds(List<Long> roleIds);
 }

+ 128 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/OssFileServiceImpl.java

@@ -1,19 +1,40 @@
 package com.hfln.portal.infrastructure.service.impl;
 
+import cn.hfln.framework.extension.BizException;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hfln.portal.domain.exception.ErrorEnum;
 import com.hfln.portal.infrastructure.mapper.TblOssFileMapper;
+import com.hfln.portal.infrastructure.oss.OssClient;
+import com.hfln.portal.infrastructure.oss.OssUtils;
 import com.hfln.portal.infrastructure.po.BasePO;
 import com.hfln.portal.infrastructure.po.TblOssFile;
 import com.hfln.portal.infrastructure.service.OssFileService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Objects;
 
 @Service
+@Slf4j
 public class OssFileServiceImpl extends ServiceImpl<TblOssFileMapper, TblOssFile> implements OssFileService {
 
+    @Autowired
+    private OssClient ossClient;
+
+    @Value("${oss.download-url-pre}")
+    private String downloadUrlPre;
+
+
     @Override
     public List<TblOssFile> queryFile(String busiType, String busiKey) {
 
@@ -39,4 +60,111 @@ public class OssFileServiceImpl extends ServiceImpl<TblOssFileMapper, TblOssFile
         }
         return this.baseMapper.selectOne(queryWrapper);
     }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public TblOssFile uploadCommonFile(MultipartFile file, String fileType, String busiKey) throws IOException {
+        // 校验文件非空
+        if (file.isEmpty()) {
+            throw new BizException(ErrorEnum.FILE_IS_EMPTY.getErrorCode(), ErrorEnum.FILE_IS_EMPTY.getErrorMessage());
+        }
+
+        // 校验文件类型参数
+        if (StringUtils.isEmpty(fileType)) {
+            throw new BizException(ErrorEnum.FILE_TYPE_IS_NULL.getErrorCode(), ErrorEnum.FILE_TYPE_IS_NULL.getErrorMessage());
+        }
+
+        // 获取原始文件名
+        String originalFilename = file.getOriginalFilename();
+        if (originalFilename == null) {
+            throw new BizException(ErrorEnum.FILE_IS_EMPTY.getErrorCode(), "文件名不能为空");
+        }
+
+        // 生成OSS对象名称,使用原始文件名
+        String objectName = this.generateCommonObjectName(fileType, originalFilename);
+
+        // 上传文件到OSS
+        ossClient.upload(file.getInputStream(), OssUtils.BUCKET_NAME, objectName);
+
+        // 查询是否已存在相同文件名和业务类型的记录
+        TblOssFile existingFile = this.queryOneFile(fileType, busiKey, originalFilename);
+        if (existingFile != null && originalFilename.equals(existingFile.getFileName())) {
+            // 如果存在相同文件名和业务类型的记录,则更新现有记录
+            log.info("发现相同文件名和业务类型的记录,执行覆盖操作:文件名={}, 业务类型={}", originalFilename, fileType);
+
+            // 删除旧的OSS文件
+            try {
+                String oldOssUrl = existingFile.getOssUrl();
+                if (oldOssUrl != null && oldOssUrl.contains("/")) {
+                    // 从完整URL中提取objectName
+                    String oldObjectName = oldOssUrl.substring(oldOssUrl.indexOf("/") + 1);
+                    // 使用bucketName/objectName格式删除
+                    String deleteUrl = OssUtils.BUCKET_NAME + "/" + oldObjectName;
+                    ossClient.delete(deleteUrl);
+                    log.info("已删除旧的OSS文件:{}", oldObjectName);
+                }
+            } catch (Exception e) {
+                log.warn("删除旧OSS文件失败,继续执行覆盖操作:{}", e.getMessage());
+            }
+
+            // 更新数据库记录
+            String fullOssUrl = downloadUrlPre + objectName;
+            existingFile.setOssUrl(fullOssUrl);
+            this.updateById(existingFile);
+
+            log.info("文件覆盖成功:文件名={}, 业务类型={}", originalFilename, fileType);
+            return existingFile;
+        } else {
+            // 如果不存在,则创建新记录
+            TblOssFile ossFile = new TblOssFile();
+            ossFile.setBusiType(fileType); // 直接使用前端传入的文件类型作为业务类型
+            ossFile.setBusiKey(busiKey);
+            ossFile.setFileName(originalFilename); // 使用原始文件名
+            String fullOssUrl = downloadUrlPre + objectName;
+            ossFile.setOssUrl(fullOssUrl);
+            this.save(ossFile);
+
+            log.info("新文件上传成功:文件名={}, OSS路径={}", originalFilename, objectName);
+            return ossFile;
+        }
+    }
+
+
+    /**
+     * 生成通用文件的OSS对象名称,保留原始文件名
+     *
+     * @param fileType         文件类型
+     * @param originalFilename 原始文件名
+     * @return OSS对象名称
+     */
+    @Override
+    public String generateCommonObjectName(String fileType, String originalFilename) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(fileType)
+                .append("/")
+                .append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
+                .append("/")
+                .append(originalFilename); // 直接使用原始文件名
+
+        return sb.toString();
+    }
+
+    /**
+     * 校验是否为有效的OTA文件
+     * @param fileName 文件名
+     * @return 是否为有效文件
+     */
+    public boolean isValidOtaFile(String fileName) {
+        if (fileName == null) {
+            return false;
+        }
+        String lowerFileName = fileName.toLowerCase();
+        // 支持常见的固件文件格式
+        return lowerFileName.endsWith(".bin") ||
+                lowerFileName.endsWith(".hex") ||
+                lowerFileName.endsWith(".fw") ||
+                lowerFileName.endsWith(".img") ||
+                lowerFileName.endsWith(".tar.gz") ||
+                lowerFileName.endsWith(".zip");
+    }
 }

+ 43 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/TblIndustryNewsServiceImpl.java

@@ -0,0 +1,43 @@
+package com.hfln.portal.infrastructure.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hfln.portal.common.request.web.QueryNewsListParam;
+import com.hfln.portal.infrastructure.mapper.TblIndustryNewsMapper;
+import com.hfln.portal.infrastructure.po.TblIndustryNews;
+import com.hfln.portal.infrastructure.service.TblIndustryNewsService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Objects;
+
+@Service
+public class TblIndustryNewsServiceImpl extends ServiceImpl<TblIndustryNewsMapper, TblIndustryNews> implements TblIndustryNewsService {
+
+    @Override
+    public List<TblIndustryNews> queryNewsList(QueryNewsListParam param) {
+        LambdaQueryWrapper<TblIndustryNews> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 新闻标题模糊搜索
+        if (param.getNewsTitle() != null){
+            queryWrapper.like(TblIndustryNews::getNewsTitle, param.getNewsTitle());
+        }
+
+        // 新闻类型模糊搜索
+        if (param.getNewsType() != null){
+            queryWrapper.like(TblIndustryNews::getNewsType, param.getNewsType());
+        }
+
+        // 根据发布时间筛选
+        if (Objects.nonNull(param.getPublishTime())) {
+            queryWrapper.ge(TblIndustryNews::getPublishTime, param.getPublishTime());
+        }
+        if (Objects.nonNull(param.getPublishTime())) {
+            queryWrapper.lt(TblIndustryNews::getPublishTime, param.getPublishTime().plusDays(1));
+        }
+
+        // 设置排序
+        queryWrapper.orderByDesc(TblIndustryNews::getPublishTime);
+        return this.baseMapper.selectList(queryWrapper);
+    }
+}

+ 13 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/TblRoleMenuMapServiceImpl.java

@@ -40,5 +40,18 @@ public class TblRoleMenuMapServiceImpl extends ServiceImpl<TblRoleMenuMapMapper,
                 .map(TblRoleMenuMap::getMenuId)
                 .collect(Collectors.toList());
     }
+
+    @Override
+    public List<Long> queryMenuIdByRoleIds(List<Long> roleIds) {
+        List<TblRoleMenuMap> list = tblRoleMenuMapMapper.selectList(
+                new LambdaQueryWrapper<TblRoleMenuMap>()
+                        .in(TblRoleMenuMap::getRoleId, roleIds)
+                        .eq(TblRoleMenuMap::getIsDeleted, 0)
+        );
+        return list.stream()
+                .map(TblRoleMenuMap::getMenuId)
+                .distinct()
+                .collect(Collectors.toList());
+    }
 }