244 lines
11 KiB
Java
244 lines
11 KiB
Java
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 对齐)。
|
||
*
|
||
* <p>与原版差异点:写操作(应用 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<AppCategory> 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<AppCategory> 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<Map> getTempList(BoaUser authUserWithDept, Integer appModId) {
|
||
File sourceFile = new File(FILE_PATH + "/tempUpload/" + appModId);
|
||
List<Map> 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<String, Object> 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<InputStreamResource> 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<UserCurrentRole> currentRoleOpt = userCurrentRoleService.findById(authUserWithDept.getId());
|
||
if (!currentRoleOpt.isPresent() || !OtherUtil.isAdminById(currentRoleOpt.get().getRoleId())) {
|
||
throw new BizException(ErrorCode.FORBIDDEN);
|
||
}
|
||
}
|
||
}
|