diff --git a/Dockerfile b/Dockerfile index a02b14a..2c7841f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,27 @@ -# 基础镜像使用Java +#指定基础镜像(一般指定程序所依赖的环境) +#本例子是构建SpringBoot,所以指定的jdk环境变量 FROM openjdk:17 +#添加维护者信息 +MAINTAINER author:chenyuepan +MAINTAINER mail:3158614516@qq.com -# 作者 -MAINTAINER yuepan +#将./hello-world.jar添加到容器的/opt目录中 +COPY ./venue_reservation_service-0.0.1-SNAPSHOT.jar /opt -# 指定容器需要映射到主机的端口 -EXPOSE 9020 - -# VOLUME 指定了临时文件目录为/tmp。 -# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp -VOLUME /tmp +#指定dockerfile的命令在哪个目录下执行 +WORKDIR /opt -# 定义 JAR_FILE 参数,默认值为打包的 JAR 文件名 -ARG JAR_FILE=venue_reservation_service-0.0.1-SNAPSHOT.jar +#指定容器和外界交互的端口 +EXPOSE 9020 -# 将jar包添加到容器中并更名为app.jar -ADD target/${JAR_FILE} /docker-venue.jar +#容器运行后执行的命令(该命令在WORKDIR指定的工作目录下执行) +#注意:多个CMD指令只有最后一个会生效 +CMD java -jar venue_reservation_service-0.0.1-SNAPSHOT.jar -#容器启动执行命令 -ENTRYPOINT ["java", "-jar", "/docker-venue.jar"] +#在构建镜像时执行的命令 +#我这边用它来打印java的环境变量 +RUN java -version +#添加该镜像的元数据 +LABEL version="1.0" description="这是一个Web服务器" by="edc" diff --git a/drone.yml b/drone.yml new file mode 100644 index 0000000..7b6e1a0 --- /dev/null +++ b/drone.yml @@ -0,0 +1,41 @@ +kind: pipeline +type: docker +name: venue_reservation_service + +steps: + - name: build-jar # 流水线名称 + image: maven:3.8.5-openjdk-17 # 定义创建容器的Docker镜像 + volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置 + - name: maven-cache + path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载 + - name: maven-build + path: /app/build # 将应用打包好的Jar和执行脚本挂载出来 + commands: # 定义在Docker容器中执行的shell命令 + - mvn clean package -DskipTests=true # 应用打包命 + - cp target/game.jar /app/build/venue_reservation_service-0.0.1-SNAPSHOT.jar + - cp Dockerfile /app/build/ + - cp run.sh /app/build/ + - name: build-docker # 流水线名称 + image: plugins/docker + volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置 + - name: maven-build + path: /app/build # 将应用打包好的Jar和执行脚本挂载出来 + - name: docker + path: /var/run/docker.sock # 挂载宿主机的docker + settings: + dockerfile: /app/build/Dockerfile + commands: # 定义在Docker容器中执行的shell命令 + - cd /app/build + - chmod +x run.sh + - sh run.sh + - docker ps +volumes: # 定义流水线挂载目录,用于共享数据 + - name: maven-build + host: + path: /home/docker/drone/maven/build # 从宿主机中挂载的目录 + - name: maven-cache + host: + path: /home/docker/drone/maven/cache + - name: docker + host: + path: /var/run/docker.sock diff --git a/pom.xml b/pom.xml index 33ed0c4..da2944c 100644 --- a/pom.xml +++ b/pom.xml @@ -202,6 +202,24 @@ fastjson 2.0.34 + + + org.springframework.boot + spring-boot-starter-amqp + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + 4.5.0 + + + diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..f04f100 --- /dev/null +++ b/run.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# 定义应用组名 +group_name='nnzmr' +# 定义应用名称 +app_name='venue_reservation_service' +# 定义应用版本 +app_version='latest' +echo '----copy jar----' +docker stop ${app_name} +echo '----stop container----' +docker rm ${app_name} +echo '----rm container----' +docker rmi ${group_name}/${app_name}:${app_version} +echo '----rm image----' +# 打包编译docker镜像 +docker build -t ${group_name}/${app_name}:${app_version} . +echo '----build image----' +docker run -p 9020:9020 --name ${app_name} \ +-e TZ="Asia/Shanghai" \ +-v /etc/localtime:/etc/localtime \ +-d ${group_name}/${app_name}:${app_version} +echo '----start container----' diff --git a/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java b/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java index 16ff701..81c117c 100644 --- a/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java +++ b/src/main/java/com/example/venue_reservation_service/VenueReservationServiceApplication.java @@ -7,7 +7,6 @@ import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -21,11 +20,11 @@ import java.util.TimeZone; @EnableAsync @SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) @MapperScan({"com.example.venue_reservation_service.mapper"}) -public class VenueReservationServiceApplication { +public class VenueReservationServiceApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("GMT+08:00")); - ConfigurableApplicationContext context = SpringApplication.run(VenueReservationServiceApplication.class, args); + SpringApplication.run(VenueReservationServiceApplication.class, args); System.out.println("(*^▽^*)启动成功!!!(〃'▽'〃)"); } @@ -51,4 +50,6 @@ public class VenueReservationServiceApplication { mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return mapper; } + + } diff --git a/src/main/java/com/example/venue_reservation_service/config/InterceptorConfig.java b/src/main/java/com/example/venue_reservation_service/config/InterceptorConfig.java index a10b86e..4ee0c4d 100644 --- a/src/main/java/com/example/venue_reservation_service/config/InterceptorConfig.java +++ b/src/main/java/com/example/venue_reservation_service/config/InterceptorConfig.java @@ -2,6 +2,7 @@ package com.example.venue_reservation_service.config; import com.example.venue_reservation_service.interceptor.JWTInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @@ -19,6 +20,9 @@ public class InterceptorConfig implements WebMvcConfigurer { .addPathPatterns("/school/**") .excludePathPatterns("/user/test") .excludePathPatterns("/user/admin") + .excludePathPatterns("/swagger-ui/index.html") + .excludePathPatterns("/doc.html") .excludePathPatterns("/user/login"); } + } diff --git a/src/main/java/com/example/venue_reservation_service/config/Knife4jConfig.java b/src/main/java/com/example/venue_reservation_service/config/Knife4jConfig.java new file mode 100644 index 0000000..19438ab --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/config/Knife4jConfig.java @@ -0,0 +1,65 @@ +package com.example.venue_reservation_service.config; + +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + *

+ * Knife4j配置类 + *

+ * + * @author Patrick + * @since 2025-1-1 + */ +@Configuration +public class Knife4jConfig { + + /** + * 创建系统分组api + * + */ + @Bean + public GroupedOpenApi systemApi() { + return createRestApi("系统管理", "com.example.venue_reservation_service.controller"); + } + + /** + * 创建api + * + * @param groupName 分组名称 + * @param basePackage 包路径 + * @return GroupedOpenApi + */ + public GroupedOpenApi createRestApi(String groupName, String basePackage) { + return GroupedOpenApi.builder() + .group(groupName) // 分组名称 + .packagesToScan(basePackage) // 扫描的包路径 + .pathsToMatch("/**") // 匹配所有路径 + .addOpenApiCustomizer(openApi -> openApi.info(apiInfo())) // 设置api信息 + .build(); + } + + /** + * api简介信息 + * + * @return ApiInfo + */ + private Info apiInfo() { + return new Info() + .title("华交场馆管控一体化平台API") // 标题 + .description("校园 e 站通・华交场馆管控一体化平台 -- 接口文档") // 描述 + .version("1.0.0") // 版本号 + .termsOfService("http://doc.xiaominfo.com") // 服务条款 + .contact(new Contact().name("实训小组1") + .url("https://github.com/Patrick-Luo-THR") + .email("patrick.luo@163.com")) // 联系人信息 + .license(new License().name("Apache 2.0") + .url("http://www.apache.org/licenses/LICENSE-2.0.html")); // 许可证信息 + } + +} + diff --git a/src/main/java/com/example/venue_reservation_service/config/RabbitMQConfig.java b/src/main/java/com/example/venue_reservation_service/config/RabbitMQConfig.java new file mode 100644 index 0000000..26866f4 --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/config/RabbitMQConfig.java @@ -0,0 +1,35 @@ +package com.example.venue_reservation_service.config; + +import org.springframework.amqp.core.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +@Configuration +public class RabbitMQConfig { + + // 定义延迟交换机(使用自定义x-delayed-message类型) + @Bean + public CustomExchange delayedExchange() { + return new CustomExchange("delayed.exchange", "x-delayed-message", + true, + false, + Map.of("x-delayed-type", "direct")); + } + + // 定义延迟队列 + @Bean + public Queue delayedQueue() { + return QueueBuilder.durable("delayed.queue").build(); + } + + // 绑定队列到交换机 + @Bean + public Binding binding(Queue delayedQueue, CustomExchange delayedExchange) { + return BindingBuilder.bind(delayedQueue) + .to(delayedExchange) + .with("delay.routing.key") + .noargs(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/controller/AdminController.java b/src/main/java/com/example/venue_reservation_service/controller/AdminController.java index 415a6e2..5919eba 100644 --- a/src/main/java/com/example/venue_reservation_service/controller/AdminController.java +++ b/src/main/java/com/example/venue_reservation_service/controller/AdminController.java @@ -1,60 +1,62 @@ package com.example.venue_reservation_service.controller; -import com.example.venue_reservation_service.domain.User; import com.example.venue_reservation_service.dto.AdminSlot; import com.example.venue_reservation_service.dto.PointDTO; import com.example.venue_reservation_service.service.AdminService; import com.example.venue_reservation_service.vo.Result; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/admin") @CrossOrigin -@Api("管理员接口") +@Tag(name = "管理员接口", description = "管理员及超级管理员专用接口,包含场馆管理、用户管理等操作") public class AdminController { @Resource private AdminService adminService; - @ApiOperation("查询管理的场馆") + @Operation(summary = "查询管理的场馆", description = "根据管理员ID查询其管理的所有场馆信息", parameters = {@Parameter(name = "userId", description = "管理员用户ID", required = true)}) @GetMapping("/query") - public Result query(@RequestParam("userId") Integer userId){ + public Result query(@RequestParam("userId") Integer userId) { return adminService.queryVenue(userId); } - @ApiOperation("管理员添加场地禁用时间") + @Operation(summary = "添加场地禁用时间", description = "设置指定场地的禁用时间段,用于维护或特殊安排", parameters = {@Parameter(name = "slot", description = "场地禁用时间对象,包含场地ID、开始/结束时间等信息", required = true)}) @PostMapping("/time") - public Result time(@RequestBody AdminSlot slot){ + public Result time(@RequestBody AdminSlot slot) { return adminService.addTime(slot); } - @ApiOperation("通过手机号查询用户") + @Operation(summary = "通过手机号查询用户", description = "根据手机号精确查询用户信息", parameters = {@Parameter(name = "phone", description = "用户手机号(11位数字)", required = true)}) @GetMapping("/select") - public Result select(@RequestParam("phone") String phone){ + public Result select(@RequestParam("phone") String phone) { return adminService.selectUser(phone); } - @ApiOperation("管理员为用户账号充值积分") + @Operation(summary = "为用户充值积分", description = "管理员为指定用户账号充值积分,支持备注说明", parameters = {@Parameter(name = "dto", description = "积分充值对象,包含用户ID、积分数量、操作备注等信息", required = true)}) @PostMapping("/point") - public Result point(@RequestBody PointDTO dto){ + public Result point(@RequestBody PointDTO dto) { return adminService.userPoint(dto); } - @ApiOperation("管理员绑定关联场馆") + @Operation(summary = "管理员绑定关联场馆", description = "将管理员与指定类型的场馆进行关联,使其可以管理该场馆", parameters = { + @Parameter(name = "userId", description = "管理员用户ID", required = true), + @Parameter(name = "typeId", description = "场馆编号", required = true)}) @PostMapping("/manage") - public Result manage(@RequestParam("userId") Integer userId, - @RequestParam("typeId") Integer typeId){ + public Result manage(@RequestParam("userId") Integer userId, @RequestParam("typeId") Integer typeId) { return adminService.manage(userId, typeId); } - @ApiOperation("移除关联场馆信息") + @Operation(summary = "移除关联场馆", description = "取消管理员与指定类型场馆的关联关系", + parameters = { + @Parameter(name = "userId", description = "管理员用户ID", required = true), + @Parameter(name = "typeId", description = "场馆编号", required = true)}) @PostMapping("/cancel") - public Result cancel(@RequestParam("userId") Integer userId, - @RequestParam("typeId") Integer typeId){ + public Result cancel(@RequestParam("userId") Integer userId, @RequestParam("typeId") Integer typeId) { return adminService.cancel(userId, typeId); } - -} +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/controller/ReservationController.java b/src/main/java/com/example/venue_reservation_service/controller/ReservationController.java index 8f87e90..3cd32e6 100644 --- a/src/main/java/com/example/venue_reservation_service/controller/ReservationController.java +++ b/src/main/java/com/example/venue_reservation_service/controller/ReservationController.java @@ -65,13 +65,13 @@ public class ReservationController { @ApiOperation("获取预约数据") @GetMapping("/data") - public Result data(@RequestParam("college") String college){ - return reservationService.queryData(college); + public Result data(){ + return Result.ok(reservationService.list()).message("数据获取成功"); } @ApiOperation("导出用户预约记录") - @GetMapping("/excel/{id}") - public void excel(@PathVariable("id") Integer userId, HttpServletResponse response){ + @GetMapping("/excel") + public void excel(@RequestParam(value = "id", required = false) Integer userId, HttpServletResponse response){ reservationService.exportExcel(response, userId); } diff --git a/src/main/java/com/example/venue_reservation_service/controller/UserController.java b/src/main/java/com/example/venue_reservation_service/controller/UserController.java index ab44b6e..e6762e3 100644 --- a/src/main/java/com/example/venue_reservation_service/controller/UserController.java +++ b/src/main/java/com/example/venue_reservation_service/controller/UserController.java @@ -1,118 +1,308 @@ package com.example.venue_reservation_service.controller; -import com.example.venue_reservation_service.annotation.Log; import com.example.venue_reservation_service.domain.User; import com.example.venue_reservation_service.dto.*; +import com.example.venue_reservation_service.mq.DelayMessageSender; import com.example.venue_reservation_service.service.UserService; import com.example.venue_reservation_service.vo.Result; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; +import java.time.LocalTime; + @RestController @RequestMapping("/user") -@Api("用户接口管理") +@Tag(name = "用户模块", description = "用户模块相关接口,包含登录、信息管理、充值等功能") @CrossOrigin public class UserController { @Resource private UserService userService; + @Autowired + private DelayMessageSender delayMessageSender; + + // 原始预约处理方法替换为发送延迟消息 + public void scheduleReservationCheck(long reservationId, LocalDate reservationDate, LocalTime end) { + delayMessageSender.sendReservationCheck(reservationId, reservationDate, end); + } + @GetMapping("/test") - public Result test(){ - return Result.ok("test").message("success"); + @Operation( + summary = "示例接口", + description = "用于测试系统通信的示例接口,发送测试消息到RabbitMQ", + parameters = {} + ) + public Result test() { + delayMessageSender.sendTestMessage(); + return Result.ok("测试消息已发送,请观察控制台输出").message("success"); } - @ApiOperation("login") + @Operation( + summary = "用户登录", + description = "通过微信小程序临时授权码获取用户OpenID完成登录", + parameters = { + @Parameter( + name = "code", + description = "微信小程序登录时获取的临时授权码(有效期5分钟)", + required = true, + schema = @Schema(type = "string"), + example = "0A3m7r000aBcDe123f456" + ) + } + ) @GetMapping("/login") - public Result getOpenId(@RequestParam("code")String code){ + public Result getOpenId(@RequestParam("code") String code) { return userService.getOpenId(code); } - @ApiOperation("管理员登录") + @Operation( + summary = "管理员登录", + description = "场馆管理员登录系统,验证账号密码", + parameters = { + @Parameter( + name = "dto", + description = "管理员登录DTO,包含账号和密码", + required = true, + schema = @Schema(implementation = AdminDTO.class) + ) + } + ) @PostMapping("/admin") -// @Log(operationType = "POST", operationDesc = "场馆管理员登录,用户账号为#{#body.account}") - public Result adminLogin(@RequestBody AdminDTO dto){ + public Result adminLogin(@RequestBody AdminDTO dto) { return userService.adminLogin(dto); } - @ApiOperation("用户个人信息完善") + @Operation( + summary = "完善用户信息", + description = "用户完善个人信息,如姓名、性别、生日等", + parameters = { + @Parameter( + name = "userInfo", + description = "用户信息DTO,包含用户基本资料", + required = true, + schema = @Schema(implementation = UserInfo.class) + ) + } + ) @PostMapping("/update") - public Result update(@RequestBody UserInfo userInfo){ + public Result update(@RequestBody UserInfo userInfo) { return userService.userUpdate(userInfo); } - @ApiOperation("获取用户列表") + @Operation( + summary = "查询用户列表", + description = "根据条件查询用户列表,支持分页和筛选", + parameters = { + @Parameter( + name = "dto", + description = "用户查询DTO,包含查询条件和分页参数", + required = true, + schema = @Schema(implementation = UserDTO.class) + ) + } + ) @PostMapping("/list") - public Result queryUserList(@RequestBody UserDTO dto){ + public Result queryUserList(@RequestBody UserDTO dto) { return userService.getUserList(dto); } - @ApiOperation("校验用户身份信息") + @Operation( + summary = "校验用户身份", + description = "校验用户提交的身份信息(如身份证、学生证等)", + parameters = { + @Parameter( + name = "dto", + description = "身份校验DTO,包含用户身份信息", + required = true, + schema = @Schema(implementation = CheckDTO.class) + ) + } + ) @PostMapping("/check") - public Result check(@RequestBody CheckDTO dto){ + public Result check(@RequestBody CheckDTO dto) { return userService.userCheck(dto); } - @ApiOperation("获取用户的认证信息") + @Operation( + summary = "获取用户认证信息", + description = "根据用户ID获取其认证信息(如学校、专业等)", + parameters = { + @Parameter( + name = "userId", + description = "用户ID", + required = true, + schema = @Schema(type = "integer") + ) + } + ) @GetMapping("/detail") - public Result get(@RequestParam("id") Integer userId){ + public Result get(@RequestParam("id") Integer userId) { return userService.getSchool(userId); } - @ApiOperation("充值") + @Operation( + summary = "用户充值", + description = "用户账户余额充值", + parameters = { + @Parameter( + name = "userId", + description = "用户ID", + required = true, + schema = @Schema(type = "integer") + ), + @Parameter( + name = "amount", + description = "充值金额(单位:元)", + required = true, + schema = @Schema(type = "number", format = "double") + ) + } + ) @PostMapping("/recharge") - public Result recharge(@RequestParam("id") Integer userId, - @RequestParam("amount") Double amount){ + public Result recharge(@RequestParam("id") Integer userId, @RequestParam("amount") Double amount) { return userService.userRecharge(userId, amount); } - @ApiOperation("用户补充手机号及邮箱") + @Operation( + summary = "补充联系方式", + description = "用户补充手机号和邮箱信息", + parameters = { + @Parameter( + name = "dto", + description = "联系方式DTO,包含手机号和邮箱", + required = true, + schema = @Schema(implementation = PhoneDTO.class) + ) + } + ) @PostMapping("/phone") - public Result check(@RequestBody PhoneDTO dto){ + public Result check(@RequestBody PhoneDTO dto) { return userService.checkPhone(dto); } - @ApiOperation("获取用户的数量") + @Operation( + summary = "获取用户数量", + description = "获取系统用户总数", + parameters = {} + ) @GetMapping("/num") - public Result num(){ + public Result num() { return userService.useNum(); } - @ApiOperation("获取管理员信息") + @Operation( + summary = "查询管理员信息", + description = "查询管理员列表,支持分页查询", + parameters = { + @Parameter( + name = "dto", + description = "分页查询DTO,包含页码和每页数量", + required = true, + schema = @Schema(implementation = PageDTO.class) + ) + } + ) @PostMapping("/admininfo") - public Result admininfo(@RequestBody PageDTO dto){ + public Result admininfo(@RequestBody PageDTO dto) { return userService.admininfo(dto); } - @ApiOperation("超级管理员添加管理员信息") + @Operation( + summary = "创建管理员", + description = "超级管理员创建新的管理员账号", + parameters = { + @Parameter( + name = "user", + description = "管理员用户对象,包含账号信息", + required = true, + schema = @Schema(implementation = User.class) + ) + } + ) @PostMapping("/create") - public Result create(@RequestBody User user){ + public Result create(@RequestBody User user) { return userService.createAdmin(user); } - @ApiOperation("移除管理员信息") + @Operation( + summary = "移除管理员", + description = "超级管理员移除指定管理员账号", + parameters = { + @Parameter( + name = "userId", + description = "待移除的管理员用户ID", + required = true, + schema = @Schema(type = "integer") + ) + } + ) @GetMapping("/remove") - public Result remove(@RequestParam("userId") Integer userId){ + public Result remove(@RequestParam("userId") Integer userId) { return userService.removeAdmin(userId); } - @ApiOperation("修改账号密码") + @Operation( + summary = "修改密码", + description = "用户修改账号密码", + parameters = { + @Parameter( + name = "dto", + description = "密码修改DTO,包含原密码和新密码", + required = true, + schema = @Schema(implementation = PasswdDTO.class) + ) + } + ) @PostMapping("/passwd") - public Result passwd(@RequestBody PasswdDTO dto){ + public Result passwd(@RequestBody PasswdDTO dto) { return userService.passwd(dto); } - @ApiOperation("用户头像上传") + @Operation( + summary = "上传头像", + description = "用户上传个人头像", + parameters = { + @Parameter( + name = "userId", + description = "用户ID", + required = true, + schema = @Schema(type = "integer") + ), + @Parameter( + name = "image", + description = "头像图片文件(支持JPG、PNG格式)", + required = true, + schema = @Schema(type = "file") + ) + } + ) @PostMapping("/upload") - public Result upload(@RequestParam("id") Integer userId, @RequestParam("image") MultipartFile file){ + public Result upload(@RequestParam("id") Integer userId, @RequestParam("image") MultipartFile file) { return userService.upload(userId, file); } - @ApiOperation("用户信息获取") + + @Operation( + summary = "获取用户信息", + description = "根据用户ID获取详细信息", + parameters = { + @Parameter( + name = "userId", + description = "用户ID", + required = true, + schema = @Schema(type = "integer") + ) + } + ) @GetMapping("/info/{id}") - public Result info(@PathVariable("id") Integer userId){ + public Result info(@PathVariable("id") Integer userId) { return userService.getUserInfo(userId); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/domain/User.java b/src/main/java/com/example/venue_reservation_service/domain/User.java index b2bbe62..6cfe9cb 100644 --- a/src/main/java/com/example/venue_reservation_service/domain/User.java +++ b/src/main/java/com/example/venue_reservation_service/domain/User.java @@ -62,8 +62,8 @@ public class User implements Serializable { /** * 账号注册时间 */ - @JsonFormat(pattern = "yyyy-MM-dd") - @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime registerTime; /** @@ -79,8 +79,8 @@ public class User implements Serializable { /** * 用户信息修改时间 */ - @JsonFormat(pattern = "yyyy-MM-dd") - @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; /** diff --git a/src/main/java/com/example/venue_reservation_service/dto/AdminDTO.java b/src/main/java/com/example/venue_reservation_service/dto/AdminDTO.java index 8b59cff..890ad9f 100644 --- a/src/main/java/com/example/venue_reservation_service/dto/AdminDTO.java +++ b/src/main/java/com/example/venue_reservation_service/dto/AdminDTO.java @@ -1,11 +1,13 @@ package com.example.venue_reservation_service.dto; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.HashMap; import java.util.Map; @Data +@Schema(description = "管理员登录对象") public class AdminDTO { private String account; @@ -14,5 +16,4 @@ public class AdminDTO { private String token; - } diff --git a/src/main/java/com/example/venue_reservation_service/dto/AdminSlot.java b/src/main/java/com/example/venue_reservation_service/dto/AdminSlot.java index 5bd69d6..6f85daa 100644 --- a/src/main/java/com/example/venue_reservation_service/dto/AdminSlot.java +++ b/src/main/java/com/example/venue_reservation_service/dto/AdminSlot.java @@ -2,6 +2,7 @@ package com.example.venue_reservation_service.dto; import com.example.venue_reservation_service.vo.AdminTime; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -9,15 +10,20 @@ import java.time.LocalDate; import java.util.List; @Data +@Schema(description = "禁用时间设置对象") public class AdminSlot { + @Schema(description = "用户编号") Integer userId; + @Schema(description = "场地编号") Integer venueId; + @Schema(description = "禁用日期") @JsonFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date; + @Schema(description = "选择的时间段") List times; } diff --git a/src/main/java/com/example/venue_reservation_service/dto/PointDTO.java b/src/main/java/com/example/venue_reservation_service/dto/PointDTO.java index 9242bc3..0544856 100644 --- a/src/main/java/com/example/venue_reservation_service/dto/PointDTO.java +++ b/src/main/java/com/example/venue_reservation_service/dto/PointDTO.java @@ -1,13 +1,18 @@ package com.example.venue_reservation_service.dto; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data +@Schema(description = "积分添加对象") public class PointDTO { + @Schema(description = "管理员编号") Integer adminId; + @Schema(description = "用户编号") Integer userId; + @Schema(description = "充值金额") Double money; } diff --git a/src/main/java/com/example/venue_reservation_service/mq/DelayMessageConsumer.java b/src/main/java/com/example/venue_reservation_service/mq/DelayMessageConsumer.java new file mode 100644 index 0000000..b718ecd --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/mq/DelayMessageConsumer.java @@ -0,0 +1,28 @@ +package com.example.venue_reservation_service.mq; + +import com.example.venue_reservation_service.service.ReservationService; +import jakarta.annotation.Resource; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Service; + +import java.time.LocalTime; + +@Service +public class DelayMessageConsumer { + + @Resource + private ReservationService reservationService; + + // 处理预约检查消息 + @RabbitListener(queues = "delayed.queue") + public void handleReservationCheck(Integer reservationId) { + reservationService.dealReservation(reservationId); + } + + // 处理测试消息 + @RabbitListener(queues = "delayed.queue") + public void handleTestMessage(String message) { + System.out.println("[" + LocalTime.now() + "] 收到延迟消息: " + message); + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/mq/DelayMessageSender.java b/src/main/java/com/example/venue_reservation_service/mq/DelayMessageSender.java new file mode 100644 index 0000000..512ac5d --- /dev/null +++ b/src/main/java/com/example/venue_reservation_service/mq/DelayMessageSender.java @@ -0,0 +1,52 @@ +package com.example.venue_reservation_service.mq; + +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessagePostProcessor; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +@Service +public class DelayMessageSender { + + @Autowired + private RabbitTemplate rabbitTemplate; + + // 发送预约检查延迟消息 + public void sendReservationCheck(long reservationId, LocalDate reservationDate, LocalTime end) { + // 计算延迟时间(毫秒) + long delayMills = calculateDelay(reservationDate, end); + + // 发送延迟消息 + sendDelayedMessage("delay.routing.key", reservationId, delayMills); + } + + // 发送15秒延迟的测试消息 + public void sendTestMessage() { + System.out.println("[" + LocalTime.now() + "] 发送测试消息,将在15秒后消费"); + sendDelayedMessage("delay.routing.key", "TEST_MESSAGE", 15000); + } + + private long calculateDelay(LocalDate date, LocalTime time) { + LocalDateTime target = LocalDateTime.of(date, time); + long targetMills = target.toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); + long currentMills = System.currentTimeMillis(); + return Math.max(0, targetMills - currentMills); + } + + private void sendDelayedMessage(String routingKey, Object message, long delayMillis) { + rabbitTemplate.convertAndSend("delayed.exchange", routingKey, message, new MessagePostProcessor() { + @Override + public Message postProcessMessage(Message message) throws AmqpException { + // 设置延迟时间(毫秒) + message.getMessageProperties().setHeader("x-delay", delayMillis); + return message; + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/venue_reservation_service/service/ReservationService.java b/src/main/java/com/example/venue_reservation_service/service/ReservationService.java index 26a695a..a8a3f56 100644 --- a/src/main/java/com/example/venue_reservation_service/service/ReservationService.java +++ b/src/main/java/com/example/venue_reservation_service/service/ReservationService.java @@ -27,8 +27,6 @@ public interface ReservationService extends IService { void dealReservation(Integer reservationId); - Result queryData(String college); - void exportExcel(HttpServletResponse response, Integer userId); } diff --git a/src/main/java/com/example/venue_reservation_service/service/impl/ReservationServiceImpl.java b/src/main/java/com/example/venue_reservation_service/service/impl/ReservationServiceImpl.java index 120ff67..66d1f4d 100644 --- a/src/main/java/com/example/venue_reservation_service/service/impl/ReservationServiceImpl.java +++ b/src/main/java/com/example/venue_reservation_service/service/impl/ReservationServiceImpl.java @@ -2,7 +2,6 @@ package com.example.venue_reservation_service.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -10,6 +9,7 @@ import com.example.venue_reservation_service.domain.*; import com.example.venue_reservation_service.dto.RAddDTO; import com.example.venue_reservation_service.dto.ReservationDTO; import com.example.venue_reservation_service.mapper.*; +import com.example.venue_reservation_service.mq.DelayMessageSender; import com.example.venue_reservation_service.service.*; import com.example.venue_reservation_service.utils.DateUtil; import com.example.venue_reservation_service.utils.DownExcel; @@ -18,6 +18,8 @@ import com.example.venue_reservation_service.vo.*; import com.example.venue_reservation_service.vo.excel.ReservationExcel; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,9 +28,7 @@ import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.temporal.TemporalAdjusters; import java.util.*; -import java.util.stream.Collectors; /** * @author 31586 @@ -73,6 +73,8 @@ public class ReservationServiceImpl extends ServiceImpl dealReservation(reservation.getId()), - reservationDate, - end - ); + +// scheduleUtil.scheduleOneTimeTask(task, reservationDate, end); +// // 定时任务解耦设计 +// scheduleUtil.scheduleOneTimeTask( +// () -> dealReservation(reservation.getId()), +// reservationDate, +// end +// ); + + // 发送延迟消息到 RabbitMQ + delayMessageSender.sendReservationCheck(reservation.getId(), reservationDate, end); // 返回订单信息 return Result.ok(payment).message("预约成功"); @@ -407,36 +413,6 @@ public class ReservationServiceImpl extends ServiceImpl xlist = new ArrayList<>(); - List ylist = new ArrayList<>(); - - // 查询每个单位的用户编号集合 - QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.select("distinct user_id") - .eq("college", college); - List userIds = schoolMapper.selectObjs(wrapper) - .stream() - .map(obj -> (Integer) obj) - .toList(); - // 查询当前单位下预约次数(最近3个月) - for (int i = 2;i>=0;i--){ - LocalDate date = LocalDate.now().minusMonths(i); - Long count = getBaseMapper().selectCount(Wrappers.lambdaQuery() - .in(Reservation::getUserId, userIds) - .between(Reservation::getReservationDate, - date.with(TemporalAdjusters.firstDayOfMonth()), - date.with(TemporalAdjusters.lastDayOfMonth()))); - xlist.add(date.getYear()+"年" + date.getMonthValue() +"月"); - ylist.add(count); - } - - - return Result.ok(new DataVO(xlist, ylist)).message("ok"); - } - @Override public void exportExcel(HttpServletResponse response, Integer userId) { ReservationMapper mapper = getBaseMapper(); @@ -447,7 +423,6 @@ public class ReservationServiceImpl extends ServiceImpl user.setUserPhone(PhoneUtil.maskPhone(user.getUserPhone())); user.setEmail(PhoneUtil.maskEmail(user.getEmail())); } + user.setBalance(0.0); + Balance balance = balanceService.getOne(Wrappers.lambdaQuery().eq(Balance::getUserId, userId)); + if (Optional.ofNullable(balance).isPresent()) { + user.setBalance(balance.getBalance()); + } user.setAvatar(imgUrl + user.getAvatar()); user.setAccessCode(""); return Result.ok(user).message("用户信息查询成功"); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 683f9ec..def2604 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -37,6 +37,30 @@ spring: min-idle: 0 jackson: date-format: yyyy-MM-dd HH:mm:ss + rabbitmq: + host: ${localhosturl} + port: 5672 + username: MQadmin + password: MQadmin + virtual-host: / + template: + retry: + enabled: true + initial-interval: 1000ms + max-interval: 10000ms + max-attempts: 3 + multiplier: 2 +# knife4j的增强配置,不需要增强可以不配 +knife4j: + enable: true # 开启knife4j,无需添加@EnableKnife4j注解 + setting: + language: zh_cn #中文 + swagger-model-name: 实体列表 #默认为: Swagger Models + basic: # 开启Swagger的Basic认证功能,默认是false + enable: false +# username: admin +# password: 123456 + weixin: app_id: wxce0025f2c0b10a8e @@ -55,6 +79,7 @@ minio: bucket: venue-sport access: url: http://${localhosturl}:9000/${minio.bucket}/ + logging: level: root: info diff --git a/src/main/resources/mapper/ReservationMapper.xml b/src/main/resources/mapper/ReservationMapper.xml index 81df52b..2d51646 100644 --- a/src/main/resources/mapper/ReservationMapper.xml +++ b/src/main/resources/mapper/ReservationMapper.xml @@ -22,16 +22,19 @@