This commit is contained in:
Chuck1sn
2025-06-16 11:26:20 +08:00
parent ea10b156e3
commit 621170b347
5 changed files with 1906 additions and 1896 deletions

View File

@@ -18,9 +18,12 @@ import io.minio.PutObjectArgs;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.security.Principal; import java.security.Principal;
import java.time.Instant;
import java.util.List; import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jooq.generated.mjga.tables.pojos.User; import org.jooq.generated.mjga.tables.pojos.User;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -41,13 +44,29 @@ public class IdentityAccessController {
private final MinioClient minioClient; private final MinioClient minioClient;
private final MinIoConfig minIoConfig; private final MinIoConfig minIoConfig;
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).WRITE_USER_ROLE_PERMISSION)")
@PostMapping( @PostMapping(
value = "/avatar/upload", value = "/avatar/upload",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.TEXT_PLAIN_VALUE) produces = MediaType.TEXT_PLAIN_VALUE)
public String uploadAvatar(Principal principal, @RequestPart("file") MultipartFile multipartFile) public String uploadAvatar(@RequestPart("file") MultipartFile multipartFile) throws Exception {
throws Exception { String originalFilename = multipartFile.getOriginalFilename();
String objectName = String.format("/avatar/%s/avatar.jpg", principal.getName()); if (StringUtils.isEmpty(originalFilename)) {
throw new BusinessException("文件名不能为空");
}
String contentType = multipartFile.getContentType();
String extension = "";
if ("image/jpeg".equals(contentType)) {
extension = ".jpg";
} else if ("image/png".equals(contentType)) {
extension = ".png";
}
String objectName =
String.format(
"/avatar/%d%s%s",
Instant.now().toEpochMilli(),
RandomStringUtils.insecure().nextAlphabetic(6),
extension);
if (multipartFile.isEmpty()) { if (multipartFile.isEmpty()) {
throw new BusinessException("上传的文件不能为空"); throw new BusinessException("上传的文件不能为空");
} }

View File

@@ -1,6 +1,5 @@
package com.zl.mjga.dto.urp; package com.zl.mjga.dto.urp;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
@AllArgsConstructor @AllArgsConstructor
@@ -9,15 +8,8 @@ import lombok.*;
@Builder @Builder
public class PermissionRespDto { public class PermissionRespDto {
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Long id; private Long id;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String code; private String code;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String name; private String name;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean isBound; private Boolean isBound;
} }

View File

@@ -1718,12 +1718,6 @@
} }
}, },
"PermissionRespDto": { "PermissionRespDto": {
"required": [
"code",
"id",
"isBound",
"name"
],
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "id": {

File diff suppressed because it is too large Load Diff

View File

@@ -54,8 +54,8 @@
</select> </select>
</div> </div>
</div> </div>
<button type="submit" @click.prevent="handleSubmit" <button type="submit" @click.prevent="handleSubmit" :disabled="uploadLoading"
class="mt-5 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"> class="mt-5 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center disabled:opacity-50 disabled:cursor-not-allowed">
保存 保存
</button> </button>
</form> </form>
@@ -84,6 +84,8 @@ const { user, onSubmit } = defineProps<{
const formData = ref(); const formData = ref();
const { uploadUserAvatar } = useUserUpsert(); const { uploadUserAvatar } = useUserUpsert();
const { showAlert } = useAlertStore(); const { showAlert } = useAlertStore();
const uploadLoading = ref(false);
const updateFormData = (newUser: typeof user) => { const updateFormData = (newUser: typeof user) => {
formData.value = { formData.value = {
@@ -104,7 +106,7 @@ const validateFile = (file?: File) => {
if (!file) { if (!file) {
throw new ValidationError("您未选择文件"); throw new ValidationError("您未选择文件");
} }
const allowedTypes = ["image/jpeg", "image/png", "image/gif"]; const allowedTypes = ["image/jpeg", "image/png"];
if (!allowedTypes.includes(file.type)) { if (!allowedTypes.includes(file.type)) {
throw new ValidationError("不支持的文件类型"); throw new ValidationError("不支持的文件类型");
} }
@@ -116,6 +118,7 @@ const validateFile = (file?: File) => {
const handleFileChange = (event: Event) => { const handleFileChange = (event: Event) => {
const file = (event.target as HTMLInputElement).files?.[0]; const file = (event.target as HTMLInputElement).files?.[0];
uploadLoading.value = true;
try { try {
validateFile(file); validateFile(file);
new Compressor(file!, { new Compressor(file!, {
@@ -125,6 +128,7 @@ const handleFileChange = (event: Event) => {
mimeType: "auto", // 自动选择最佳格式 mimeType: "auto", // 自动选择最佳格式
success: async (compressedFile: File) => { success: async (compressedFile: File) => {
formData.value.avatar = await uploadUserAvatar(compressedFile); formData.value.avatar = await uploadUserAvatar(compressedFile);
uploadLoading.value = false;
showAlert({ showAlert({
content: "上传成功", content: "上传成功",
level: "success", level: "success",
@@ -136,6 +140,7 @@ const handleFileChange = (event: Event) => {
}); });
} catch (error) { } catch (error) {
(event.target as HTMLInputElement).value = ""; (event.target as HTMLInputElement).value = "";
uploadLoading.value = false;
throw error; throw error;
} }
}; };