gitea + docker + drone 实现CI/CD

master
chenyuepan 3 weeks ago
parent 25113aba37
commit a3e36cba7e

@ -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"

@ -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

@ -202,6 +202,24 @@
<artifactId>fastjson</artifactId>
<version>2.0.34</version>
</dependency>
<!-- Spring Boot Starter for RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- Java 8 时间支持 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- Swagger3-knife4j依赖 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!-- 封装了各大模型的交互接口 -->
<!-- <dependency>-->
<!-- <groupId>io.springboot.ai</groupId>-->

@ -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----'

@ -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;
@ -25,7 +24,7 @@ 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;
}
}

@ -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");
}
}

@ -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;
/**
* <p>
* Knife4j
* </p>
*
* @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")); // 许可证信息
}
}

@ -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();
}
}

@ -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);
}
}

@ -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);
}

@ -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);
}
}

@ -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;
/**

@ -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;
}

@ -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<AdminTime> times;
}

@ -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;
}

@ -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);
}
}

@ -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;
}
});
}
}

@ -27,8 +27,6 @@ public interface ReservationService extends IService<Reservation> {
void dealReservation(Integer reservationId);
Result queryData(String college);
void exportExcel(HttpServletResponse response, Integer userId);
}

@ -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<ReservationMapper, Reser
@Resource
private VenueSlotService venueSlotService;
@Resource
private DelayMessageSender delayMessageSender;
@Transactional
@ -196,13 +198,17 @@ public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reser
// 处理当前预约
dealReservation(reservation.getId());
};
scheduleUtil.scheduleOneTimeTask(task, reservationDate, end);
// 定时任务解耦设计
scheduleUtil.scheduleOneTimeTask(
() -> 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<ReservationMapper, Reser
return Result.ok(detail).message("ok");
}
@Override
public Result queryData(String college) {
List<String> xlist = new ArrayList<>();
List<Long> ylist = new ArrayList<>();
// 查询每个单位的用户编号集合
QueryWrapper<School> wrapper = new QueryWrapper<>();
wrapper.select("distinct user_id")
.eq("college", college);
List<Integer> 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.<Reservation>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<ReservationMapper, Reser
}
}
}

@ -398,6 +398,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
user.setUserPhone(PhoneUtil.maskPhone(user.getUserPhone()));
user.setEmail(PhoneUtil.maskEmail(user.getEmail()));
}
user.setBalance(0.0);
Balance balance = balanceService.getOne(Wrappers.<Balance>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("用户信息查询成功");

@ -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

@ -23,15 +23,18 @@
<select id="selectByUserId" parameterType="java.lang.Integer" resultType="com.example.venue_reservation_service.vo.excel.ReservationExcel">
SELECT
id,
user_id AS userId,
venue_id AS venueId,
start_time AS startTime,
end_time AS endTime,
reservation_date AS reservationDate
user_id ,
venue_id ,
start_time ,
end_time ,
reservation_date
FROM
venue_reservation
WHERE
<where>
<if test="userId != null and userId != ''">
user_id = #{userId}
</if>
</where>
</select>
</mapper>

Loading…
Cancel
Save