package com.yunhai.userhome.controller.api; import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.model.BoaUser; import com.yunhai.userhome.config.Config; import com.yunhai.userhome.entity.AppCategory; import com.yunhai.userhome.entity.UserCurrentRole; import com.yunhai.userhome.service.IAppCategoryService; import com.yunhai.userhome.service.IUserCurrentRoleService; import com.yunhai.userhome.utils.FileUtil; import com.yunhai.userhome.utils.OtherUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.InputStreamResource; import org.springframework.data.domain.Page; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.UUID; /** * 应用设置控制器(自 systemsetting AppSettingController + SysController 迁移,1:1 对齐)。 * *

与原版差异点:写操作(应用 CRUD / UpdateResDate / 模板上传保存)校验调用者 * 为超管(type==1)或校管理员(roleId∈{2,5,7})——原版任何登录用户可写,因新入口仅管理员可见,收紧。 * 查询(分页列表 / FindAppCategory / GetTempList / downloadTemp)登录即可。 */ @RestController @RequestMapping(Config.API_PATH) public class AppSettingController { @Value("${FILE_PATH:./upload/}") private String FILE_PATH; @Value("${FILE_WEB_PATH:}") private String FILE_WEB_PATH; @Autowired private IAppCategoryService appCategoryService; @Autowired private IUserCurrentRoleService userCurrentRoleService; // ==================== 应用类别 CRUD ==================== /** 新增/编辑应用(JSON body,1:1 对齐原 SaveAppCategory) */ @PostMapping(value = "SaveAppCategory", produces = "application/json") public void saveAppCategory(BoaUser authUserWithDept, @RequestBody AppCategory appCategory) { checkAdmin(authUserWithDept); if (appCategory.getCDate() == null) appCategory.setCDate(new Date()); if (appCategory.getResDate() == null) appCategory.setResDate(new Date()); appCategory.setCId(authUserWithDept.getId()); appCategoryService.save(appCategory); } @PostMapping(value = "DelAppCategory", produces = "application/json") public void delAppCategory(BoaUser authUserWithDept, Integer id) { checkAdmin(authUserWithDept); appCategoryService.deleteById(id); } @GetMapping(value = "GetAppCategoryList") public Map getAppCategoryList(BoaUser authUserWithDept, Integer page, Integer count, @RequestParam(value = "roleType") Integer roleType, @RequestParam(value = "name", required = false) String name) { Page data = appCategoryService.findPageList(page, count, roleType, name); Map map = new HashMap(); map.put("total", data.getTotalElements()); map.put("list", data.getContent()); return map; } @GetMapping(value = "FindAppCategory") public Optional findAppCategory(BoaUser authUserWithDept, Integer id) { return appCategoryService.findById(id); } @PostMapping(value = "UpdateResDate", produces = "application/json") public void updateResDate(BoaUser authUserWithDept, Integer id) { checkAdmin(authUserWithDept); appCategoryService.updateResDate(id); } // ==================== 模板管理(对齐原 SysController) ==================== /** 上传模板压缩包(zip/rar),解压覆盖到 tempUpload/<appModId>/,删除压缩包 */ @PostMapping(value = "tempUpload", produces = "application/json") public void tempUpload(BoaUser authUserWithDept, @RequestParam("file") MultipartFile file, Integer appModId) { checkAdmin(authUserWithDept); if (file == null || file.isEmpty()) { throw new BizException(ErrorCode.CUSTOM, "请选择上传文件"); } String originalName = file.getOriginalFilename(); String ext = originalName == null ? "" : originalName.substring(originalName.lastIndexOf(".")).toLowerCase(Locale.ROOT); if (!".zip".equals(ext) && !".rar".equals(ext)) { throw new BizException(ErrorCode.CUSTOM, "仅支持 zip/rar 格式"); } // 不走图片 5MB 校验(模板包允许更大,依赖 multipart 配置),存到 tempUpload 子目录 String uploadPath = FILE_PATH + "/tempUpload/"; File fl = new File(uploadPath); if (!fl.exists()) fl.mkdirs(); String fileName = FileUtil.uploadFile(file, uploadPath); String filePath = "/upload/tempUpload/" + fileName; File unzipFiles = new File(FILE_PATH + "/tempUpload/" + appModId); if (!unzipFiles.exists()) unzipFiles.mkdirs(); else FileUtil.delAllFile(unzipFiles.getPath()); File zipFile = new File(filePath.replace("/upload", FILE_PATH)); try { FileUtil.zipDecompress(zipFile, unzipFiles); } catch (Exception e) { throw new BizException(ErrorCode.CUSTOM, e.getMessage()); } finally { zipFile.delete(); } } /** 模板文件列表(name + 访问 path) */ @GetMapping(value = "GetTempList") public List getTempList(BoaUser authUserWithDept, Integer appModId) { File sourceFile = new File(FILE_PATH + "/tempUpload/" + appModId); List result = new ArrayList<>(); if (sourceFile.exists() && sourceFile.isDirectory() && sourceFile.listFiles() != null) { Arrays.stream(sourceFile.listFiles()).forEach(file -> { Map map = new HashMap(); map.put("name", file.getName()); map.put("path", FILE_WEB_PATH + "/tempUpload/" + appModId + "/" + file.getName()); result.add(map); }); } return result; } /** 保存模板文件内容(新增 fileName 路径穿越校验,原版没有;content 走 JSON body 避免 URL 长度限制) */ @PostMapping(value = "SaveTempFile", produces = "application/json") public void saveTempFile(BoaUser authUserWithDept, @RequestBody Map body) { checkAdmin(authUserWithDept); Object appModIdObj = body.get("appModId"); String fileName = body.get("fileName") == null ? null : body.get("fileName").toString(); String content = body.get("content") == null ? "" : body.get("content").toString(); if (appModIdObj == null) { throw new BizException(ErrorCode.CUSTOM, "缺少appModId"); } Integer appModId; try { appModId = Integer.parseInt(appModIdObj.toString()); } catch (NumberFormatException e) { throw new BizException(ErrorCode.CUSTOM, "非法appModId"); } if (fileName == null || fileName.contains("/") || fileName.contains("\\") || fileName.contains("..")) { throw new BizException(ErrorCode.CUSTOM, "非法文件名"); } File dir = new File(FILE_PATH + "/tempUpload/" + appModId); if (!dir.exists()) dir.mkdirs(); File file = new File(FILE_PATH + "/tempUpload/" + appModId + "/" + fileName); try { if (!file.exists()) { file.createNewFile(); } FileOutputStream fos = new FileOutputStream(file); fos.write(content.getBytes()); fos.flush(); fos.close(); } catch (IOException e) { throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "保存失败"); } } /** 打包下载模板目录(zip 流式下载,临时 zip 关闭即删) */ @GetMapping(value = "downloadTemp", produces = "application/json;charset=UTF-8") public ResponseEntity downloadTemp(BoaUser authUserWithDept, Integer appModId, String name) { File zipFilePath = new File(FILE_PATH + "/tempUpload/" + appModId); if (!zipFilePath.exists() || !zipFilePath.isDirectory()) { throw new BizException(ErrorCode.CUSTOM, "模板不存在"); } String zipName = UUID.randomUUID().toString().substring(0, 8) + ".zip"; File zipFile = new File(FILE_PATH + "/tempUpload/" + zipName); try { FileUtil.zipCompress(zipFilePath, zipFile); } catch (Exception e) { throw new BizException(ErrorCode.CUSTOM, e.getMessage()); } String downloadName = (name == null || name.trim().isEmpty()) ? "template" : name; try { InputStream inputStream = new FileInputStream(zipFile) { @Override public void close() throws IOException { super.close(); zipFile.delete(); } }; HttpHeaders headers = new HttpHeaders(); headers.add("Cache-Control", "no-cache, no-store, must-revalidate"); headers.add("Content-Disposition", "attachment; filename=" + new String((downloadName + ".zip").getBytes("gbk"), "iso8859-1")); headers.add("Pragma", "no-cache"); headers.add("Expires", "0"); return ResponseEntity.ok().headers(headers) .contentType(MediaType.parseMediaType("application/octet-stream")) .body(new InputStreamResource(inputStream)); } catch (IOException e) { zipFile.delete(); throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "下载失败"); } } // ==================== 私有方法 ==================== /** * 写操作权限校验:超管(type==1) 或 校管理员(roleId∈{2,5,7}),其余 403。 */ private void checkAdmin(BoaUser authUserWithDept) { if (authUserWithDept.getType() == 1) { return; } Optional currentRoleOpt = userCurrentRoleService.findById(authUserWithDept.getId()); if (!currentRoleOpt.isPresent() || !OtherUtil.isAdminById(currentRoleOpt.get().getRoleId())) { throw new BizException(ErrorCode.FORBIDDEN); } } }