From 7f10e9f311f5be0be97090f970ba8894431f5934 Mon Sep 17 00:00:00 2001 From: lapuda <577732344@qq.com> Date: Thu, 25 Jun 2026 14:42:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9C=BA=E5=9C=B0=E5=B1=95?= =?UTF-8?q?=E4=BD=8D=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E6=8B=AC=E5=B1=95=E4=BD=8D=E5=9B=BE=E6=9F=A5=E8=AF=A2=E3=80=81?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=80=81=E9=A2=84=E5=AE=9A=E5=8F=8A=E8=B0=83?= =?UTF-8?q?=E6=8D=A2=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=95=B0=E6=8D=AE=E6=A8=A1=E5=9E=8B=E5=8F=8A=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E8=A1=A8=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/cms/VenueInfoController.java | 198 ++++++++++++++++++ .../cms/domain/OutdoorFairBoothBooking.java | 38 ++++ .../java/com/ruoyi/cms/domain/VenueBooth.java | 32 +++ .../cms/domain/VenueBoothSwapRequest.java | 10 + .../mapper/OutdoorFairBoothBookingMapper.java | 8 + .../ruoyi/cms/mapper/VenueBoothMapper.java | 24 +++ sql/cms_venue_booth_map.sql | 86 ++++++++ 7 files changed, 396 insertions(+) create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/OutdoorFairBoothBooking.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBooth.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBoothSwapRequest.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/OutdoorFairBoothBookingMapper.java create mode 100644 ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/VenueBoothMapper.java create mode 100644 sql/cms_venue_booth_map.sql diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/VenueInfoController.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/VenueInfoController.java index 46c431e..c8e4414 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/VenueInfoController.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/controller/cms/VenueInfoController.java @@ -1,15 +1,30 @@ package com.ruoyi.cms.controller.cms; import java.util.Arrays; +import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.cms.domain.VenueInfo; +import com.ruoyi.cms.domain.OutdoorFair; +import com.ruoyi.cms.domain.OutdoorFairBoothBooking; +import com.ruoyi.cms.domain.VenueBooth; import com.ruoyi.cms.domain.VenueInfoDictDataRequest; +import com.ruoyi.cms.domain.VenueBoothSwapRequest; +import com.ruoyi.cms.mapper.CompanyMapper; +import com.ruoyi.cms.mapper.OutdoorFairBoothBookingMapper; +import com.ruoyi.cms.mapper.OutdoorFairMapper; +import com.ruoyi.cms.mapper.VenueBoothMapper; import com.ruoyi.cms.service.IVenueInfoService; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.Company; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.StringUtils; @@ -17,6 +32,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -41,6 +57,18 @@ public class VenueInfoController extends BaseController @Autowired private IVenueInfoService venueInfoService; + @Autowired + private VenueBoothMapper venueBoothMapper; + + @Autowired + private OutdoorFairMapper outdoorFairMapper; + + @Autowired + private OutdoorFairBoothBookingMapper outdoorFairBoothBookingMapper; + + @Autowired + private CompanyMapper companyMapper; + /** * 查询场地信息列表 */ @@ -115,6 +143,176 @@ public class VenueInfoController extends BaseController return toAjax(venueInfoService.insertVenueInfoDictData(request.getDictType(), request.getDictLabel(), getUsername())); } + @ApiOperation("查询场地展位图") + @PreAuthorize("@ss.hasAnyPermi('cms:venueInfo:query,cms:outdoorFair:query')") + @GetMapping("/{venueId}/booths") + public AjaxResult listBooths(@PathVariable Long venueId) + { + List list = venueBoothMapper.selectList(new LambdaQueryWrapper() + .eq(VenueBooth::getVenueId, venueId) + .orderByAsc(VenueBooth::getPositionY) + .orderByAsc(VenueBooth::getPositionX) + .orderByAsc(VenueBooth::getBoothNumber)); + return success(list); + } + + @ApiOperation("保存场地展位图") + @PreAuthorize("@ss.hasPermi('cms:venueInfo:edit')") + @Transactional(rollbackFor = Exception.class) + @PostMapping("/{venueId}/booths") + public AjaxResult saveBooths(@PathVariable Long venueId, @RequestBody List booths) + { + venueBoothMapper.physicalDeleteDeletedByVenueId(venueId); + List existingBooths = venueBoothMapper.selectList(new LambdaQueryWrapper() + .eq(VenueBooth::getVenueId, venueId)); + Map existingById = existingBooths.stream() + .filter(item -> item.getId() != null) + .collect(Collectors.toMap(VenueBooth::getId, item -> item, (a, b) -> a)); + Map existingByNumber = existingBooths.stream() + .filter(item -> StringUtils.isNotBlank(item.getBoothNumber())) + .collect(Collectors.toMap(VenueBooth::getBoothNumber, item -> item, (a, b) -> a)); + Set boothNumbers = new HashSet<>(); + Set savedIds = new HashSet<>(); + if (booths != null) { + for (VenueBooth booth : booths) { + if (StringUtils.isBlank(booth.getBoothNumber())) { + return AjaxResult.error("展位号不能为空"); + } + if (!boothNumbers.add(booth.getBoothNumber())) { + return AjaxResult.error("展位号不能重复:" + booth.getBoothNumber()); + } + } + for (VenueBooth booth : booths) { + VenueBooth existing = booth.getId() == null ? null : existingById.get(booth.getId()); + if (existing == null) { + existing = existingByNumber.get(booth.getBoothNumber()); + } + booth.setVenueId(venueId); + if (existing == null) { + booth.setId(null); + venueBoothMapper.insert(booth); + } else { + booth.setId(existing.getId()); + venueBoothMapper.updateById(booth); + } + if (booth.getId() != null) { + savedIds.add(booth.getId()); + } + } + VenueInfo venueInfo = venueInfoService.getById(venueId); + if (venueInfo != null) { + venueInfo.setFloorCount(booths.size()); + venueInfoService.updateById(venueInfo); + } + } + List removedIds = existingBooths.stream() + .map(VenueBooth::getId) + .filter(id -> id != null && !savedIds.contains(id)) + .collect(Collectors.toList()); + if (!removedIds.isEmpty()) { + venueBoothMapper.physicalDeleteByVenueIdAndIds(venueId, removedIds); + } + return success(); + } + + @ApiOperation("查询户外招聘会展位图") + @PreAuthorize("@ss.hasPermi('cms:outdoorFair:query')") + @GetMapping("/fair/{fairId}/booth-map") + public AjaxResult fairBoothMap(@PathVariable Long fairId) + { + OutdoorFair fair = outdoorFairMapper.selectById(fairId); + if (fair == null || fair.getVenueId() == null) { + return success(new ArrayList<>()); + } + List booths = venueBoothMapper.selectList(new LambdaQueryWrapper() + .eq(VenueBooth::getVenueId, fair.getVenueId()) + .orderByAsc(VenueBooth::getPositionY) + .orderByAsc(VenueBooth::getPositionX) + .orderByAsc(VenueBooth::getBoothNumber)); + List bookings = outdoorFairBoothBookingMapper.selectList(new LambdaQueryWrapper() + .eq(OutdoorFairBoothBooking::getFairId, fairId)); + Map bookingMap = bookings.stream().collect(Collectors.toMap(OutdoorFairBoothBooking::getBoothId, item -> item, (a, b) -> a)); + List> result = new ArrayList<>(); + for (VenueBooth booth : booths) { + OutdoorFairBoothBooking booking = bookingMap.get(booth.getId()); + Map row = new java.util.HashMap<>(); + row.put("id", booth.getId()); + row.put("boothId", booth.getId()); + row.put("boothNumber", booth.getBoothNumber()); + row.put("positionX", booth.getPositionX()); + row.put("positionY", booth.getPositionY()); + row.put("bookingId", booking == null ? null : booking.getId()); + row.put("companyId", booking == null ? null : booking.getCompanyId()); + row.put("companyName", booking == null ? null : booking.getCompanyName()); + row.put("status", booking == null ? "available" : booking.getStatus()); + result.add(row); + } + return success(result); + } + + @ApiOperation("预定户外招聘会展位") + @PreAuthorize("@ss.hasPermi('cms:outdoorFair:edit')") + @PostMapping("/fair/{fairId}/booth-booking") + public AjaxResult bookBooth(@PathVariable Long fairId, @RequestBody OutdoorFairBoothBooking booking) + { + if (booking.getCompanyId() == null) { + return AjaxResult.error("请选择预定单位"); + } + Company company = companyMapper.selectById(booking.getCompanyId()); + if (company == null || company.getStatus() == null || company.getStatus() != 1) { + return AjaxResult.error("只能预定审核通过的企业"); + } + VenueBooth booth = venueBoothMapper.selectById(booking.getBoothId()); + booking.setFairId(fairId); + booking.setBoothNumber(booth == null ? booking.getBoothNumber() : booth.getBoothNumber()); + booking.setCompanyName(company.getName()); + booking.setStatus(StringUtils.isBlank(booking.getStatus()) ? "reserved" : booking.getStatus()); + OutdoorFairBoothBooking existing = outdoorFairBoothBookingMapper.selectOne(new LambdaQueryWrapper() + .eq(OutdoorFairBoothBooking::getFairId, fairId) + .eq(OutdoorFairBoothBooking::getBoothId, booking.getBoothId()) + .last("limit 1")); + if (existing == null) { + return toAjax(outdoorFairBoothBookingMapper.insert(booking)); + } + booking.setId(existing.getId()); + return toAjax(outdoorFairBoothBookingMapper.updateById(booking)); + } + + @ApiOperation("取消户外招聘会展位预定") + @PreAuthorize("@ss.hasPermi('cms:outdoorFair:edit')") + @DeleteMapping("/fair/{fairId}/booth-booking/{boothId}") + public AjaxResult cancelBooth(@PathVariable Long fairId, @PathVariable Long boothId) + { + return toAjax(outdoorFairBoothBookingMapper.delete(new LambdaQueryWrapper() + .eq(OutdoorFairBoothBooking::getFairId, fairId) + .eq(OutdoorFairBoothBooking::getBoothId, boothId))); + } + + @ApiOperation("调换户外招聘会展位") + @PreAuthorize("@ss.hasPermi('cms:outdoorFair:edit')") + @PostMapping("/fair/{fairId}/booth-swap") + public AjaxResult swapBooth(@PathVariable Long fairId, @RequestBody VenueBoothSwapRequest request) + { + OutdoorFairBoothBooking first = outdoorFairBoothBookingMapper.selectOne(new LambdaQueryWrapper() + .eq(OutdoorFairBoothBooking::getFairId, fairId) + .eq(OutdoorFairBoothBooking::getBoothId, request.getFirstBoothId()).last("limit 1")); + OutdoorFairBoothBooking second = outdoorFairBoothBookingMapper.selectOne(new LambdaQueryWrapper() + .eq(OutdoorFairBoothBooking::getFairId, fairId) + .eq(OutdoorFairBoothBooking::getBoothId, request.getSecondBoothId()).last("limit 1")); + if (first == null || second == null) { + return AjaxResult.error("请选择两个已预定展位"); + } + Long companyId = first.getCompanyId(); + String companyName = first.getCompanyName(); + first.setCompanyId(second.getCompanyId()); + first.setCompanyName(second.getCompanyName()); + second.setCompanyId(companyId); + second.setCompanyName(companyName); + outdoorFairBoothBookingMapper.updateById(first); + outdoorFairBoothBookingMapper.updateById(second); + return success(); + } + private void fillHandleFields(VenueInfo venueInfo) { if (venueInfo.getHandleDate() == null) { diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/OutdoorFairBoothBooking.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/OutdoorFairBoothBooking.java new file mode 100644 index 0000000..94fd298 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/OutdoorFairBoothBooking.java @@ -0,0 +1,38 @@ +package com.ruoyi.cms.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("户外招聘会展位预定") +@TableName("cms_outdoor_fair_booth_booking") +public class OutdoorFairBoothBooking extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @ApiModelProperty("招聘会ID") + private Long fairId; + + @ApiModelProperty("场地展位ID") + private Long boothId; + + @ApiModelProperty("展位号") + private String boothNumber; + + @ApiModelProperty("企业ID") + private Long companyId; + + @ApiModelProperty("企业名称") + private String companyName; + + @ApiModelProperty("预定状态") + private String status; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBooth.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBooth.java new file mode 100644 index 0000000..c948bd7 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBooth.java @@ -0,0 +1,32 @@ +package com.ruoyi.cms.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("场地展位") +@TableName("cms_venue_booth") +public class VenueBooth extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @ApiModelProperty("场地ID") + private Long venueId; + + @ApiModelProperty("展位号") + private String boothNumber; + + @ApiModelProperty("X坐标") + private Integer positionX; + + @ApiModelProperty("Y坐标") + private Integer positionY; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBoothSwapRequest.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBoothSwapRequest.java new file mode 100644 index 0000000..b72797b --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/domain/VenueBoothSwapRequest.java @@ -0,0 +1,10 @@ +package com.ruoyi.cms.domain; + +import lombok.Data; + +@Data +public class VenueBoothSwapRequest +{ + private Long firstBoothId; + private Long secondBoothId; +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/OutdoorFairBoothBookingMapper.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/OutdoorFairBoothBookingMapper.java new file mode 100644 index 0000000..4736774 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/OutdoorFairBoothBookingMapper.java @@ -0,0 +1,8 @@ +package com.ruoyi.cms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.cms.domain.OutdoorFairBoothBooking; + +public interface OutdoorFairBoothBookingMapper extends BaseMapper +{ +} diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/VenueBoothMapper.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/VenueBoothMapper.java new file mode 100644 index 0000000..f2fe589 --- /dev/null +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/mapper/VenueBoothMapper.java @@ -0,0 +1,24 @@ +package com.ruoyi.cms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.cms.domain.VenueBooth; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface VenueBoothMapper extends BaseMapper +{ + @Delete("DELETE FROM cms_venue_booth WHERE venue_id = #{venueId} AND del_flag <> '0'") + int physicalDeleteDeletedByVenueId(@Param("venueId") Long venueId); + + @Delete({ + "" + }) + int physicalDeleteByVenueIdAndIds(@Param("venueId") Long venueId, @Param("ids") List ids); +} diff --git a/sql/cms_venue_booth_map.sql b/sql/cms_venue_booth_map.sql new file mode 100644 index 0000000..51f719b --- /dev/null +++ b/sql/cms_venue_booth_map.sql @@ -0,0 +1,86 @@ +-- 场地展位图与户外招聘会展位预定 +-- 瀚高数据库 / PostgreSQL 兼容 + +CREATE SEQUENCE IF NOT EXISTS "cms_venue_booth_id_seq" START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; +CREATE TABLE IF NOT EXISTS "cms_venue_booth" ( + "id" BIGINT NOT NULL DEFAULT nextval('"cms_venue_booth_id_seq"') PRIMARY KEY, + "venue_id" BIGINT NOT NULL, + "booth_number" VARCHAR(50) NOT NULL, + "position_x" INTEGER NOT NULL DEFAULT 0, + "position_y" INTEGER NOT NULL DEFAULT 0, + "create_by" VARCHAR(64) DEFAULT '', + "create_time" TIMESTAMP(0), + "update_by" VARCHAR(64) DEFAULT '', + "update_time" TIMESTAMP(0), + "remark" VARCHAR(500), + "del_flag" CHAR(1) DEFAULT '0' +); + +COMMENT ON TABLE "cms_venue_booth" IS '场地展位图'; +COMMENT ON COLUMN "cms_venue_booth"."venue_id" IS '场地ID'; +COMMENT ON COLUMN "cms_venue_booth"."booth_number" IS '展位号'; +COMMENT ON COLUMN "cms_venue_booth"."position_x" IS 'X坐标'; +COMMENT ON COLUMN "cms_venue_booth"."position_y" IS 'Y坐标'; + +CREATE UNIQUE INDEX IF NOT EXISTS "uk_cms_venue_booth_number" ON "cms_venue_booth" ("venue_id", "booth_number"); +CREATE INDEX IF NOT EXISTS "idx_cms_venue_booth_venue" ON "cms_venue_booth" ("venue_id"); + +CREATE SEQUENCE IF NOT EXISTS "cms_outdoor_fair_booth_booking_id_seq" START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; +CREATE TABLE IF NOT EXISTS "cms_outdoor_fair_booth_booking" ( + "id" BIGINT NOT NULL DEFAULT nextval('"cms_outdoor_fair_booth_booking_id_seq"') PRIMARY KEY, + "fair_id" BIGINT NOT NULL, + "booth_id" BIGINT NOT NULL, + "booth_number" VARCHAR(50) NOT NULL, + "company_id" BIGINT, + "company_name" VARCHAR(200), + "status" VARCHAR(30) DEFAULT 'reserved', + "create_by" VARCHAR(64) DEFAULT '', + "create_time" TIMESTAMP(0), + "update_by" VARCHAR(64) DEFAULT '', + "update_time" TIMESTAMP(0), + "remark" VARCHAR(500), + "del_flag" CHAR(1) DEFAULT '0' +); + +COMMENT ON TABLE "cms_outdoor_fair_booth_booking" IS '户外招聘会展位预定'; +CREATE UNIQUE INDEX IF NOT EXISTS "uk_cms_outdoor_fair_booth_booking" ON "cms_outdoor_fair_booth_booking" ("fair_id", "booth_id"); +CREATE INDEX IF NOT EXISTS "idx_cms_outdoor_fair_booth_booking_fair" ON "cms_outdoor_fair_booth_booking" ("fair_id"); + +WITH target_venue AS ( + SELECT "id" AS venue_id + FROM "cms_venue_info" + WHERE "venue_name" = '石河子市人民广场户外招聘区' + LIMIT 1 +), +seed AS ( + SELECT + target_venue.venue_id, + 'KJ' || LPAD(gs::text, 2, '0') AS booth_number, + 40 + ((gs - 1) % 20) * 54 AS position_x, + 40 + ((gs - 1) / 20) * 54 AS position_y + FROM target_venue + CROSS JOIN generate_series(1, 80) gs +) +INSERT INTO "cms_venue_booth" ("venue_id", "booth_number", "position_x", "position_y", "create_by", "create_time", "del_flag") +SELECT seed.venue_id, seed.booth_number, seed.position_x, seed.position_y, 'system', NOW(), '0' +FROM seed +WHERE NOT EXISTS ( + SELECT 1 + FROM "cms_venue_booth" existing + WHERE existing."venue_id" = seed.venue_id + AND existing."booth_number" = seed.booth_number +); + +UPDATE "cms_venue_info" venue +SET "floor_count" = booth_counts.total +FROM ( + SELECT "venue_id", COUNT(*) AS total + FROM "cms_venue_booth" + GROUP BY "venue_id" +) booth_counts +WHERE venue."id" = booth_counts."venue_id"; + +UPDATE "cms_outdoor_fair" fair +SET "booth_count" = venue."floor_count" +FROM "cms_venue_info" venue +WHERE fair."venue_id" = venue."id";