公告基础功能

This commit is contained in:
attiya 2025-09-19 14:46:51 +08:00
parent 6bdfbe07e6
commit 3727198264
34 changed files with 559 additions and 81 deletions

Binary file not shown.

View File

@ -0,0 +1,10 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="61" name="Java" />
</Languages>
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,27 @@
package com.cdzy.activity.component;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 微信配置类
*
* @author attiya
*/
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "image")
public class ImageConfig {
/**
* 活动封面地址
*/
private String activityUrl;
/**
* 公告封面地址
*/
private String bulletinUrl;
}

View File

@ -1,48 +0,0 @@
package com.cdzy.activity.component;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.dialect.DbType;
import com.mybatisflex.core.keygen.KeyGenerators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author attiya
*/
@Configuration
public class MyBatisFlexConfiguration {
private static final Logger logger = LoggerFactory.getLogger("mybatis-flex-sql:");
public MyBatisFlexConfiguration() {
// 只保留审计功能配置这部分可以留在构造函数
AuditManager.setAuditEnable(true);
AuditManager.setMessageCollector(auditMessage ->
logger.info("{},{}ms", auditMessage.getFullSql(), auditMessage.getElapsedTime())
);
}
@Bean
public FlexGlobalConfig flexGlobalConfig() {
FlexGlobalConfig globalConfig = FlexGlobalConfig.getDefaultConfig();
// 1. 数据库方言
globalConfig.setDbType(DbType.MYSQL);
// 2. 全局主键配置从构造函数移到这里
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
keyConfig.setKeyType(KeyType.Generator);
keyConfig.setValue(KeyGenerators.snowFlakeId);
keyConfig.setBefore(true);
globalConfig.setKeyConfig(keyConfig);
logger.info("MyBatis-Flex 全局配置已完成,使用 MySQL 方言和雪花算法");
return globalConfig;
}
}

View File

@ -29,6 +29,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
.addInclude("/**")
// 开放地址
.addExclude("/user/**")
.addExclude("/api/**")
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/staff/login", r -> StpUtil.checkLogin());

View File

@ -11,7 +11,6 @@ import com.mybatisflex.core.util.StringUtil;
import jakarta.annotation.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
@ -42,18 +41,6 @@ public class ActivityController {
activityService.saveActivity(activity);
return JsonResult.success();
}
//
// /**
// * 添加
// *
// * @param file 封面文件
// * @return {@code 200} 添加成功{@code 500} 添加失败
// */
// @PostMapping("upload")
// public JsonResult<?> upload(MultipartFile file) throws IOException {
// activityService.saveActivity(activity);
// return JsonResult.success();
// }
/**
* 根据主键删除

View File

@ -0,0 +1,106 @@
package com.cdzy.activity.controller;
import com.cdzy.activity.model.Bulletin;
import com.cdzy.activity.model.JsonResult;
import com.cdzy.activity.model.PageParam;
import com.cdzy.activity.model.vo.BulletinVo;
import com.cdzy.activity.service.BulletinService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.util.StringUtil;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.cdzy.activity.model.table.BulletinTableDef.BULLETIN;
/**
* 公告控制层
*
* @author attiya
* @since 2025-09-18
*/
@RestController
@RequestMapping("/bulletin")
public class BulletinController {
@Resource
private BulletinService bulletinService;
/**
* 保存
*
* @param bulletin 公告基础信息
* @return {@code true} 保存成功{@code false} 保存失败
*/
@PostMapping(value = "save",consumes = "multipart/*", headers = "content-type=multipart/form-data")
public JsonResult<?> save(BulletinVo bulletin) throws IOException {
bulletinService.saveBulletin(bulletin);
return JsonResult.success();
}
/**
* 根据主键删除
*
* @param id 主键
* @return {@code true} 删除成功{@code false} 删除失败
*/
@DeleteMapping("remove/{id}")
public JsonResult<?> remove(@PathVariable Long id) {
bulletinService.removeById(id);
return JsonResult.success();
}
/**
* 根据主键更新
*
* @param bulletin 基础信息
* @return {@code true} 更新成功{@code false} 更新失败
*/
@PutMapping(value = "update",consumes = "multipart/*", headers = "content-type=multipart/form-data")
public JsonResult<?> update(BulletinVo bulletin) throws IOException {
bulletinService.updateBulletind(bulletin);
return JsonResult.success();
}
/**
* 查询所有
*
* @return 所有数据
*/
@GetMapping("list")
public JsonResult<?> list() {
List<Bulletin> list = bulletinService.list();
return JsonResult.success(list);
}
/**
* 根据主键获取
*
* @param id 主键
* @return 详情
*/
@GetMapping("getInfo/{id}")
public JsonResult<?> getInfo(@PathVariable Long id) {
Bulletin bulletin = bulletinService.getById(id);
return JsonResult.success(bulletin);
}
/**
* 分页查询
* @param pageParam 分页参数
* @param bulletinTitle 公告标题
* @return 分页结果
*/
@GetMapping("page")
public JsonResult<?> page(PageParam pageParam, String bulletinTitle) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(BULLETIN.BULLETIN_TITLE.like(bulletinTitle, StringUtil.hasText(bulletinTitle)));
Page<Bulletin> page = bulletinService.page(pageParam.getPage(), queryWrapper);
return JsonResult.success(page);
}
}

View File

@ -0,0 +1,58 @@
package com.cdzy.activity.controller;
import com.cdzy.activity.component.ImageConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.net.MalformedURLException;
import java.nio.file.Path;
/**
* 图片预览控制层
*/
@RestController
@RequestMapping("/api/images")
public class ImagePreviewController {
@Autowired
private ImageConfig imageConfig;
/**
* 活动封面预览
* @param filename 封面文件名称
* @return 图片
* @throws MalformedURLException 预览错误
*/
@GetMapping(value = "/activity/{filename}", produces = {
MediaType.IMAGE_JPEG_VALUE,
MediaType.IMAGE_PNG_VALUE,
MediaType.IMAGE_GIF_VALUE
})
public Resource previewActivityImage(@PathVariable String filename) throws MalformedURLException {
Path filePath = Path.of(imageConfig.getActivityUrl(), filename);
return new UrlResource(filePath.toUri());
}
/**
* 公告封面预览
* @param filename 封面文件名称
* @return 图片
* @throws MalformedURLException 预览错误
*/
@GetMapping(value = "/bulletin/{filename}", produces = {
MediaType.IMAGE_JPEG_VALUE,
MediaType.IMAGE_PNG_VALUE,
MediaType.IMAGE_GIF_VALUE
})
public Resource previewBulletinImage(@PathVariable String filename) throws MalformedURLException {
Path filePath = Path.of(imageConfig.getBulletinUrl(), filename);
return new UrlResource(filePath.toUri());
}
}

View File

@ -1,16 +0,0 @@
package com.cdzy.activity.controller;
import com.cdzy.activity.model.JsonResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@RestController
public class TestController {
@GetMapping("/test")
public JsonResult<?> test() {
return JsonResult.success(LocalDateTime.now());
}
}

View File

@ -0,0 +1,14 @@
package com.cdzy.activity.mapper;
import com.mybatisflex.core.BaseMapper;
import com.cdzy.activity.model.Bulletin;
/**
* 映射层
*
* @author attiya
* @since 2025-09-18
*/
public interface BulletinMapper extends BaseMapper<Bulletin> {
}

View File

@ -0,0 +1,50 @@
package com.cdzy.activity.model;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.io.Serial;
import com.mybatisflex.core.keygen.KeyGenerators;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 实体类
*
* @author attiya
* @since 2025-09-18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("bulletin")
public class Bulletin implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id(keyType = KeyType.Generator, value = KeyGenerators.snowFlakeId)
private Long bulletinId;
/**
* 公告标题
*/
private String bulletinTitle;
/**
* 封面
*/
private String bulletinCover;
/**
* 内容
*/
private String bulletinDescription;
}

View File

@ -0,0 +1,48 @@
package com.cdzy.activity.model.vo;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.keygen.KeyGenerators;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
import java.io.Serial;
import java.io.Serializable;
/**
* 实体类
*
* @author attiya
* @since 2025-09-18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BulletinVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long bulletinId;
/**
* 公告标题
*/
private String bulletinTitle;
/**
* 封面
*/
private MultipartFile bulletinCover;
/**
* 内容
*/
private String bulletinDescription;
}

View File

@ -0,0 +1,20 @@
package com.cdzy.activity.service;
import com.cdzy.activity.model.vo.BulletinVo;
import com.mybatisflex.core.service.IService;
import com.cdzy.activity.model.Bulletin;
import java.io.IOException;
/**
* 服务层
*
* @author attiya
* @since 2025-09-18
*/
public interface BulletinService extends IService<Bulletin> {
void saveBulletin(BulletinVo bulletin) throws IOException;
void updateBulletind(BulletinVo bulletin) throws IOException;
}

View File

@ -1,16 +1,22 @@
package com.cdzy.activity.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import com.cdzy.activity.component.ImageConfig;
import com.cdzy.activity.enums.ActivityStatus;
import com.cdzy.activity.mapper.ActivityMapper;
import com.cdzy.activity.model.Activity;
import com.cdzy.activity.model.vo.ActivityVo;
import com.cdzy.activity.service.ActivityService;
import com.cdzy.activity.uitls.FileUtils;
import com.cdzy.activity.uitls.ImageToBase64Converter;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
@ -29,9 +35,20 @@ public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity>
@Resource
private ActivityMapper activityMapper;
@Resource
private ImageConfig imageConfig;
@Override
public void saveActivity(ActivityVo activity) throws IOException {
String convert = ImageToBase64Converter.convertToBase64(activity.getActivityCover());
MultipartFile activityCover = activity.getActivityCover();
String convert = null;
if (activityCover != null){
String uuid = UUID.randomUUID().toString();
String fileExtension = FileUtils.getFileExtension(activityCover);
String destPath = imageConfig.getActivityUrl()+"/"+uuid+fileExtension;
FileUtils.copyMultipartFile(activityCover,destPath,true);
convert = uuid + fileExtension;
}
Activity entity = Activity.builder()
.activityName(activity.getActivityName())
.activityCover(convert)
@ -44,6 +61,7 @@ public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity>
.endTime(activity.getEndTime())
.maxNum(activity.getMaxNum())
.limitPeople(activity.getLimitPeople())
.limitRegister(activity.getLimitRegister())
.status(ActivityStatus.UN_START_REGISTERING)
.build();
activityMapper.insert(entity);
@ -54,7 +72,16 @@ public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity>
Activity entity = activityMapper.selectOneById(activity.getActivityId());
if (entity != null) {
entity.setActivityName(activity.getActivityName());
String convert = ImageToBase64Converter.convertToBase64(activity.getActivityCover());
MultipartFile activityCover = activity.getActivityCover();
String convert = entity.getActivityCover();
if (activityCover != null){
String uuid = UUID.randomUUID().toString();
String fileExtension = FileUtils.getFileExtension(activityCover);
String destPath = imageConfig.getActivityUrl()+"/"+uuid+fileExtension;
FileUtils.copyMultipartFile(activityCover,destPath,true);
FileUtil.del(imageConfig.getActivityUrl()+"/"+convert);
convert = uuid + fileExtension;
}
entity.setActivityCover(convert);
entity.setActivitySponsor(activity.getActivitySponsor());
entity.setActivityDescription(activity.getActivityDescription());
@ -65,6 +92,7 @@ public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity>
entity.setEndTime(activity.getEndTime());
entity.setMaxNum(activity.getMaxNum());
entity.setLimitPeople(activity.getLimitPeople());
entity.setLimitRegister(activity.getLimitRegister());
activityMapper.update(entity);
}else {
throw new RuntimeException("该活动不存在");

View File

@ -0,0 +1,75 @@
package com.cdzy.activity.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import com.cdzy.activity.component.ImageConfig;
import com.cdzy.activity.model.vo.ActivityVo;
import com.cdzy.activity.model.vo.BulletinVo;
import com.cdzy.activity.uitls.FileUtils;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cdzy.activity.model.Bulletin;
import com.cdzy.activity.mapper.BulletinMapper;
import com.cdzy.activity.service.BulletinService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 服务层实现
*
* @author attiya
* @since 2025-09-18
*/
@Service
public class BulletinServiceImpl extends ServiceImpl<BulletinMapper, Bulletin> implements BulletinService {
@Resource
private BulletinMapper bulletinMapper;
@Resource
private ImageConfig imageConfig;
@Override
public void saveBulletin(BulletinVo bulletin) throws IOException {
MultipartFile activityCover = bulletin.getBulletinCover();
String convert = null;
if (activityCover != null) {
String uuid = UUID.randomUUID().toString();
String fileExtension = FileUtils.getFileExtension(activityCover);
String destPath = imageConfig.getActivityUrl() + "/" + uuid + fileExtension;
FileUtils.copyMultipartFile(activityCover, destPath, true);
convert = uuid + fileExtension;
}
Bulletin entity = Bulletin.builder()
.bulletinCover(convert)
.bulletinTitle(bulletin.getBulletinTitle())
.bulletinDescription(bulletin.getBulletinDescription())
.build();
bulletinMapper.insert(entity);
}
@Override
public void updateBulletind(BulletinVo bulletin) throws IOException {
Bulletin entity = bulletinMapper.selectOneById(bulletin.getBulletinId());
if (entity != null) {
entity.setBulletinTitle(bulletin.getBulletinTitle());
MultipartFile activityCover = bulletin.getBulletinCover();
String convert = entity.getBulletinCover();
if (activityCover != null) {
String uuid = UUID.randomUUID().toString();
String fileExtension = FileUtils.getFileExtension(activityCover);
String destPath = imageConfig.getActivityUrl() + "/" + uuid + fileExtension;
FileUtils.copyMultipartFile(activityCover, destPath, true);
FileUtil.del(imageConfig.getActivityUrl() + "/" + convert);
convert = uuid + fileExtension;
}
entity.setBulletinCover(convert);
entity.setBulletinDescription(bulletin.getBulletinDescription());
bulletinMapper.update(entity);
} else {
throw new RuntimeException("该公告不存在");
}
}
}

View File

@ -0,0 +1,57 @@
package com.cdzy.activity.uitls;
import cn.hutool.core.io.FileUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
public class FileUtils {
/**
* 安全获取文件后缀包含点符号
* @param file MultipartFile对象
* @return ".jpg" 或空字符串
*/
public static String getFileExtension(MultipartFile file) {
return Optional.ofNullable(file.getOriginalFilename())
.filter(name -> name.contains("."))
.map(name -> name.substring(name.lastIndexOf(".")))
.orElse("");
}
/**
* 获取无点的纯后缀小写形式
* @param file MultipartFile对象
* @return "jpg" 或空字符串
*/
public static String getFileExtensionWithoutDot(MultipartFile file) {
return getFileExtension(file).replace(".", "").toLowerCase();
}
/**
* 复制MultipartFile到指定路径自动创建目录
*
* @param file 源文件MultipartFile类型
* @param destPath 目标绝对路径包含文件名
* @param override 是否覆盖已存在文件
* @throws IOException 文件操作异常
*/
public static void copyMultipartFile(MultipartFile file, String destPath, boolean override) throws IOException {
// 校验目标路径
if (destPath == null || destPath.trim().isEmpty()) {
throw new IllegalArgumentException("Destination path cannot be empty");
}
// 创建父目录如果不存在
File destFile = new File(destPath);
FileUtil.mkParentDirs(destFile);
// 执行文件复制
if (override || !destFile.exists()) {
file.transferTo(destFile);
}
}
}

View File

@ -67,4 +67,6 @@ wechat:
appid: wx327d788d7bd6eddf
app-secret: adf2539a6c26499c67b5a3829f2e05e3
image:
activity-url: D:/file/activity
bulletin-url: D:/file/bulletin

View File

@ -67,4 +67,6 @@ wechat:
appid: wx327d788d7bd6eddf
app-secret: adf2539a6c26499c67b5a3829f2e05e3
image:
activity-url: D:/file/activity
bulletin-url: D:/file/bulletin

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,57 @@
package com.cdzy.activity.model.table;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.table.TableDef;
// Auto generate by mybatis-flex, do not modify it.
public class BulletinTableDef extends TableDef {
/**
* 实体类
@author attiya
@since 2025-09-18
*/
public static final BulletinTableDef BULLETIN = new BulletinTableDef();
public final QueryColumn BULLETIN_ID = new QueryColumn(this, "bulletin_id");
/**
* 封面
*/
public final QueryColumn BULLETIN_COVER = new QueryColumn(this, "bulletin_cover");
/**
* 公告标题
*/
public final QueryColumn BULLETIN_TITLE = new QueryColumn(this, "bulletin_title");
/**
* 内容
*/
public final QueryColumn BULLETIN_DESCRIPTION = new QueryColumn(this, "bulletin_description");
/**
* 所有字段
*/
public final QueryColumn ALL_COLUMNS = new QueryColumn(this, "*");
/**
* 默认字段不包含逻辑删除或者 large 等字段
*/
public final QueryColumn[] DEFAULT_COLUMNS = new QueryColumn[]{BULLETIN_ID, BULLETIN_COVER, BULLETIN_TITLE, BULLETIN_DESCRIPTION};
public BulletinTableDef() {
super("", "bulletin");
}
private BulletinTableDef(String schema, String name, String alisa) {
super(schema, name, alisa);
}
public BulletinTableDef as(String alias) {
String key = getNameWithSchema() + "." + alias;
return getCache(key, k -> new BulletinTableDef("", "bulletin", alias));
}
}