feat: Add Outdoor Fair Attendee and Device management
- Implemented Outdoor Fair Attendee and Device entities, including their respective controllers, services, and mappers. - Added statistics endpoint for Outdoor Fair data, allowing aggregation by day, week, month, or year. - Introduced SQL scripts for creating tables and mock data for attendees and devices. - Created a write-capable script for executing DML/DDL statements against the SHZ HighGo database. - Enhanced the existing query capabilities to support both read and write operations.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.ruoyi.cms.domain.OutdoorFairAttendee;
|
||||
import com.ruoyi.cms.service.IOutdoorFairAttendeeService;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
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.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 户外招聘会签到人员 Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-06-25
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/cms/outdoor-fair-attendee")
|
||||
@Api(tags = "后台:户外招聘会签到人员")
|
||||
public class OutdoorFairAttendeeController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IOutdoorFairAttendeeService outdoorFairAttendeeService;
|
||||
|
||||
/**
|
||||
* 查询签到人员列表(支持按人员姓名、手机号模糊查询)
|
||||
*/
|
||||
@ApiOperation("查询签到人员列表")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(OutdoorFairAttendee query)
|
||||
{
|
||||
startPage();
|
||||
List<OutdoorFairAttendee> list = outdoorFairAttendeeService.selectAttendeeList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签到人员详细信息
|
||||
*/
|
||||
@ApiOperation("获取签到人员详细信息")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(outdoorFairAttendeeService.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增签到人员
|
||||
*/
|
||||
@ApiOperation("新增签到人员")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:add')")
|
||||
@Log(title = "户外招聘会签到人员", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody OutdoorFairAttendee attendee)
|
||||
{
|
||||
return toAjax(outdoorFairAttendeeService.save(attendee));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改签到人员
|
||||
*/
|
||||
@ApiOperation("修改签到人员")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:edit')")
|
||||
@Log(title = "户外招聘会签到人员", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody OutdoorFairAttendee attendee)
|
||||
{
|
||||
return toAjax(outdoorFairAttendeeService.updateById(attendee));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除签到人员
|
||||
*/
|
||||
@ApiOperation("删除签到人员")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:remove')")
|
||||
@Log(title = "户外招聘会签到人员", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(outdoorFairAttendeeService.removeByIds(Arrays.asList(ids)));
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,20 @@ package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.cms.domain.FairCompany;
|
||||
@@ -35,6 +45,7 @@ import com.ruoyi.common.utils.StringUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -106,6 +117,225 @@ public class OutdoorFairController extends BaseController
|
||||
return success(outdoorFairService.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 招聘会数据统计(dashboard)
|
||||
* 按年/周/月/日维度,统计举办时间落在区间内的招聘会:
|
||||
* - 累计发布招聘会数、参会单位数、职位数;
|
||||
* - 招聘会数同比(去年同期等长区间)、环比(紧邻的上一等长区间);
|
||||
* - 各时间桶的招聘会数/参会单位数/职位数序列(用于曲线图)。
|
||||
* granularity 取值:day/week/month/year,默认 month。未传时间时统计全部招聘会
|
||||
* (“累计”口径),曲线图区间按数据实际举办时间的最小/最大值生成。
|
||||
*/
|
||||
@ApiOperation("招聘会数据统计")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:list')")
|
||||
@GetMapping("/statistics")
|
||||
public AjaxResult statistics(
|
||||
String granularity,
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime)
|
||||
{
|
||||
String gran = (StringUtils.isEmpty(granularity) ? "month" : granularity.toLowerCase());
|
||||
boolean hasRange = startTime != null || endTime != null;
|
||||
// 区间右端含当天:start 为当天 00:00,end 为次日 00:00(开区间)
|
||||
LocalDate startLd = null;
|
||||
LocalDate endLd = null;
|
||||
Date start = null;
|
||||
Date end = null;
|
||||
if (hasRange) {
|
||||
endLd = endTime != null ? toLocalDate(endTime) : LocalDate.now();
|
||||
startLd = startTime != null ? toLocalDate(startTime)
|
||||
: endLd.minusMonths(11).withDayOfMonth(1);
|
||||
start = toDate(startLd.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
end = toDate(endLd.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
// 区间内招聘会(未传区间时查全部,用于“累计”口径)
|
||||
LambdaQueryWrapper<OutdoorFair> fairWrapper = new LambdaQueryWrapper<OutdoorFair>()
|
||||
.orderByAsc(OutdoorFair::getHoldTime);
|
||||
if (start != null) {
|
||||
fairWrapper.ge(OutdoorFair::getHoldTime, start);
|
||||
}
|
||||
if (end != null) {
|
||||
fairWrapper.lt(OutdoorFair::getHoldTime, end);
|
||||
}
|
||||
List<OutdoorFair> fairs = outdoorFairService.list(fairWrapper);
|
||||
|
||||
// 未传区间时,按数据实际举办时间的最小/最大值生成曲线图区间
|
||||
if (!hasRange) {
|
||||
LocalDate minD = null;
|
||||
LocalDate maxD = null;
|
||||
for (OutdoorFair f : fairs) {
|
||||
if (f.getHoldTime() == null) {
|
||||
continue;
|
||||
}
|
||||
LocalDate d = toLocalDate(f.getHoldTime());
|
||||
if (minD == null || d.isBefore(minD)) {
|
||||
minD = d;
|
||||
}
|
||||
if (maxD == null || d.isAfter(maxD)) {
|
||||
maxD = d;
|
||||
}
|
||||
}
|
||||
if (minD == null) {
|
||||
startLd = LocalDate.now().withDayOfMonth(1);
|
||||
endLd = LocalDate.now();
|
||||
} else {
|
||||
startLd = minD;
|
||||
endLd = maxD;
|
||||
}
|
||||
}
|
||||
|
||||
// 每场参会单位/职位数(一次查回,内存分组,避免逐场 N 次查询)
|
||||
Map<Long, Long> companyByFair = new HashMap<>();
|
||||
Map<Long, Long> jobByFair = new HashMap<>();
|
||||
if (!fairs.isEmpty()) {
|
||||
List<Long> fairIds = fairs.stream().map(OutdoorFair::getId).collect(Collectors.toList());
|
||||
for (FairCompany fc : fairCompanyMapper.selectList(
|
||||
new LambdaQueryWrapper<FairCompany>().in(FairCompany::getJobFairId, fairIds))) {
|
||||
companyByFair.merge(fc.getJobFairId(), 1L, Long::sum);
|
||||
}
|
||||
for (OutdoorFairJob ojf : outdoorFairJobMapper.selectList(
|
||||
new LambdaQueryWrapper<OutdoorFairJob>().in(OutdoorFairJob::getFairId, fairIds))) {
|
||||
jobByFair.merge(ojf.getFairId(), 1L, Long::sum);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成区间内全部时间桶(含空桶),按桶累加
|
||||
List<LocalDate> buckets = buildBuckets(startLd, endLd, gran);
|
||||
Map<LocalDate, long[]> agg = new TreeMap<>();
|
||||
for (LocalDate b : buckets) {
|
||||
agg.put(b, new long[]{0, 0, 0});
|
||||
}
|
||||
long fairTotal = 0L, companyTotal = 0L, jobTotal = 0L;
|
||||
for (OutdoorFair f : fairs) {
|
||||
if (f.getHoldTime() == null) {
|
||||
continue;
|
||||
}
|
||||
LocalDate b = bucketOf(toLocalDate(f.getHoldTime()), gran);
|
||||
long[] arr = agg.get(b);
|
||||
if (arr == null) {
|
||||
continue;
|
||||
}
|
||||
long cc = companyByFair.getOrDefault(f.getId(), 0L);
|
||||
long jc = jobByFair.getOrDefault(f.getId(), 0L);
|
||||
arr[0] += 1;
|
||||
arr[1] += cc;
|
||||
arr[2] += jc;
|
||||
fairTotal += 1;
|
||||
companyTotal += cc;
|
||||
jobTotal += jc;
|
||||
}
|
||||
|
||||
DateTimeFormatter fmt = formatterFor(gran);
|
||||
List<Map<String, Object>> series = new ArrayList<>();
|
||||
for (LocalDate b : buckets) {
|
||||
long[] arr = agg.get(b);
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("time", b.format(fmt));
|
||||
row.put("fairCount", arr[0]);
|
||||
row.put("companyCount", arr[1]);
|
||||
row.put("jobCount", arr[2]);
|
||||
series.add(row);
|
||||
}
|
||||
|
||||
// 同比:去年同期等长区间;环比:紧邻的上一等长区间
|
||||
long yoyPrev = countFairs(startLd.minusYears(1), endLd.minusYears(1));
|
||||
long rangeDays = ChronoUnit.DAYS.between(startLd, endLd) + 1;
|
||||
long momPrev = countFairs(startLd.minusDays(rangeDays), endLd.minusDays(rangeDays));
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("granularity", gran);
|
||||
result.put("totals", buildTotals(fairTotal, companyTotal, jobTotal));
|
||||
result.put("yoy", buildRate(fairTotal, yoyPrev));
|
||||
result.put("mom", buildRate(fairTotal, momPrev));
|
||||
result.put("series", series);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/** 统计 [startLd, endLd](含当天)区间内的招聘会数 */
|
||||
private long countFairs(LocalDate startLd, LocalDate endLd) {
|
||||
Date ds = toDate(startLd.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
Date de = toDate(endLd.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
return outdoorFairService.count(new LambdaQueryWrapper<OutdoorFair>()
|
||||
.ge(OutdoorFair::getHoldTime, ds).lt(OutdoorFair::getHoldTime, de));
|
||||
}
|
||||
|
||||
private LocalDate toLocalDate(Date d) {
|
||||
return d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
|
||||
private Date toDate(Instant instant) {
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
/** 按粒度取所属时间桶的起始日期 */
|
||||
private LocalDate bucketOf(LocalDate ld, String gran) {
|
||||
switch (gran) {
|
||||
case "day":
|
||||
return ld;
|
||||
case "week":
|
||||
return ld.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
|
||||
case "year":
|
||||
return ld.withDayOfYear(1);
|
||||
default:
|
||||
return ld.withDayOfMonth(1);
|
||||
}
|
||||
}
|
||||
|
||||
/** 生成区间内按粒度的全部时间桶(含空桶) */
|
||||
private List<LocalDate> buildBuckets(LocalDate startLd, LocalDate endLd, String gran) {
|
||||
List<LocalDate> out = new ArrayList<>();
|
||||
LocalDate cur = bucketOf(startLd, gran);
|
||||
LocalDate endBucket = bucketOf(endLd, gran);
|
||||
while (!cur.isAfter(endBucket)) {
|
||||
out.add(cur);
|
||||
switch (gran) {
|
||||
case "day":
|
||||
cur = cur.plusDays(1);
|
||||
break;
|
||||
case "week":
|
||||
cur = cur.plusWeeks(1);
|
||||
break;
|
||||
case "year":
|
||||
cur = cur.plusYears(1);
|
||||
break;
|
||||
default:
|
||||
cur = cur.plusMonths(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private DateTimeFormatter formatterFor(String gran) {
|
||||
switch (gran) {
|
||||
case "day":
|
||||
return DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
case "week":
|
||||
return DateTimeFormatter.ofPattern("yyyy-'W'ww");
|
||||
case "year":
|
||||
return DateTimeFormatter.ofPattern("yyyy");
|
||||
default:
|
||||
return DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTotals(long fair, long company, long job) {
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("fairCount", fair);
|
||||
m.put("companyCount", company);
|
||||
m.put("jobCount", job);
|
||||
return m;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildRate(long current, long previous) {
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("fairCount", current);
|
||||
m.put("previousFairCount", previous);
|
||||
m.put("rate", previous == 0 ? null : (double) (current - previous) / (double) previous);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增户外招聘会
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.ruoyi.cms.domain.OutdoorFairDevice;
|
||||
import com.ruoyi.cms.service.IOutdoorFairDeviceService;
|
||||
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.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
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.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 户外招聘会设备码 Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-06-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/cms/outdoor-fair-device")
|
||||
@Api(tags = "后台:户外招聘会设备码")
|
||||
public class OutdoorFairDeviceController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IOutdoorFairDeviceService outdoorFairDeviceService;
|
||||
|
||||
/**
|
||||
* 查询设备码列表(支持按设备码、企业名称模糊查询)
|
||||
*/
|
||||
@ApiOperation("查询设备码列表")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(OutdoorFairDevice query)
|
||||
{
|
||||
startPage();
|
||||
List<OutdoorFairDevice> list = outdoorFairDeviceService.selectDeviceList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备码详细信息
|
||||
*/
|
||||
@ApiOperation("获取设备码详细信息")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(outdoorFairDeviceService.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成设备码(UUID)
|
||||
* 请求体:{ fairId, quantity }
|
||||
*/
|
||||
@ApiOperation("批量生成设备码")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:add')")
|
||||
@Log(title = "户外招聘会设备码", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/batch")
|
||||
public AjaxResult batch(@RequestBody Map<String, Object> request)
|
||||
{
|
||||
Long fairId = request.get("fairId") == null ? null : Long.valueOf(String.valueOf(request.get("fairId")));
|
||||
Integer quantity = request.get("quantity") == null ? null : Integer.valueOf(String.valueOf(request.get("quantity")));
|
||||
List<OutdoorFairDevice> devices = outdoorFairDeviceService.batchGenerate(fairId, quantity);
|
||||
AjaxResult result = AjaxResult.success("生成成功");
|
||||
result.put("data", devices);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定/解绑参会企业
|
||||
* 请求体:{ companyId },companyId 为空表示解绑
|
||||
*/
|
||||
@ApiOperation("绑定/解绑参会企业")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:edit')")
|
||||
@Log(title = "户外招聘会设备码", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/{id}/bind")
|
||||
public AjaxResult bind(@PathVariable("id") Long id, @RequestBody Map<String, Object> request)
|
||||
{
|
||||
Long companyId = request.get("companyId") == null ? null : Long.valueOf(String.valueOf(request.get("companyId")));
|
||||
return toAjax(outdoorFairDeviceService.bindCompany(id, companyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备码
|
||||
*/
|
||||
@ApiOperation("删除设备码")
|
||||
@PreAuthorize("@ss.hasPermi('cms:outdoorFair:remove')")
|
||||
@Log(title = "户外招聘会设备码", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(outdoorFairDeviceService.removeByIds(Arrays.asList(ids)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 户外招聘会签到人员。
|
||||
*/
|
||||
@Data
|
||||
@TableName("shz.cms_outdoor_fair_attendee")
|
||||
public class OutdoorFairAttendee extends BaseEntity
|
||||
{
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 招聘会ID */
|
||||
private Long fairId;
|
||||
|
||||
/** 人员姓名 */
|
||||
private String name;
|
||||
|
||||
/** 手机号 */
|
||||
private String phone;
|
||||
|
||||
/** 住址 */
|
||||
private String address;
|
||||
|
||||
/** 签到时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date checkInTime;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 户外招聘会设备码。
|
||||
*/
|
||||
@Data
|
||||
@TableName("shz.cms_outdoor_fair_device")
|
||||
public class OutdoorFairDevice extends BaseEntity
|
||||
{
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 招聘会ID */
|
||||
private Long fairId;
|
||||
|
||||
/** 设备码(UUID) */
|
||||
private String deviceCode;
|
||||
|
||||
/** 绑定的参会企业ID */
|
||||
private Long companyId;
|
||||
|
||||
/** 绑定的参会企业名称(快照) */
|
||||
private String companyName;
|
||||
|
||||
/** 绑定时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date bindTime;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.OutdoorFairAttendee;
|
||||
|
||||
/**
|
||||
* 户外招聘会签到人员 Mapper。
|
||||
*/
|
||||
public interface OutdoorFairAttendeeMapper extends BaseMapper<OutdoorFairAttendee>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.cms.domain.OutdoorFairDevice;
|
||||
|
||||
/**
|
||||
* 户外招聘会设备码 Mapper。
|
||||
*/
|
||||
public interface OutdoorFairDeviceMapper extends BaseMapper<OutdoorFairDevice>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ruoyi.cms.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.ruoyi.cms.domain.OutdoorFairAttendee;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 户外招聘会签到人员 Service。
|
||||
*/
|
||||
public interface IOutdoorFairAttendeeService extends IService<OutdoorFairAttendee>
|
||||
{
|
||||
/**
|
||||
* 查询签到人员列表(支持按人员姓名、手机号模糊查询)。
|
||||
*/
|
||||
List<OutdoorFairAttendee> selectAttendeeList(OutdoorFairAttendee query);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ruoyi.cms.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.ruoyi.cms.domain.OutdoorFairDevice;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 户外招聘会设备码 Service。
|
||||
*/
|
||||
public interface IOutdoorFairDeviceService extends IService<OutdoorFairDevice>
|
||||
{
|
||||
/**
|
||||
* 查询设备码列表(支持按设备码、企业名称模糊查询)。
|
||||
*/
|
||||
List<OutdoorFairDevice> selectDeviceList(OutdoorFairDevice query);
|
||||
|
||||
/**
|
||||
* 批量生成设备码(UUID)。
|
||||
*
|
||||
* @param fairId 招聘会ID
|
||||
* @param quantity 生成数量
|
||||
* @return 已生成的设备码列表
|
||||
*/
|
||||
List<OutdoorFairDevice> batchGenerate(Long fairId, Integer quantity);
|
||||
|
||||
/**
|
||||
* 绑定/解绑参会企业(companyId 为空表示解绑)。
|
||||
*/
|
||||
boolean bindCompany(Long id, Long companyId);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.ruoyi.cms.domain.OutdoorFairAttendee;
|
||||
import com.ruoyi.cms.mapper.OutdoorFairAttendeeMapper;
|
||||
import com.ruoyi.cms.service.IOutdoorFairAttendeeService;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 户外招聘会签到人员 Service 实现。
|
||||
*/
|
||||
@Service
|
||||
public class OutdoorFairAttendeeServiceImpl
|
||||
extends ServiceImpl<OutdoorFairAttendeeMapper, OutdoorFairAttendee>
|
||||
implements IOutdoorFairAttendeeService
|
||||
{
|
||||
@Override
|
||||
public List<OutdoorFairAttendee> selectAttendeeList(OutdoorFairAttendee query)
|
||||
{
|
||||
LambdaQueryWrapper<OutdoorFairAttendee> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(query.getFairId() != null, OutdoorFairAttendee::getFairId, query.getFairId());
|
||||
wrapper.like(StringUtils.isNotBlank(query.getName()), OutdoorFairAttendee::getName, query.getName());
|
||||
wrapper.like(StringUtils.isNotBlank(query.getPhone()), OutdoorFairAttendee::getPhone, query.getPhone());
|
||||
wrapper.orderByDesc(OutdoorFairAttendee::getCheckInTime);
|
||||
wrapper.orderByDesc(OutdoorFairAttendee::getId);
|
||||
return list(wrapper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.ruoyi.cms.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.ruoyi.cms.domain.FairCompany;
|
||||
import com.ruoyi.cms.domain.OutdoorFairDevice;
|
||||
import com.ruoyi.cms.mapper.CompanyMapper;
|
||||
import com.ruoyi.cms.mapper.FairCompanyMapper;
|
||||
import com.ruoyi.cms.mapper.OutdoorFairDeviceMapper;
|
||||
import com.ruoyi.cms.service.IOutdoorFairDeviceService;
|
||||
import com.ruoyi.common.core.domain.entity.Company;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 户外招聘会设备码 Service 实现。
|
||||
*/
|
||||
@Service
|
||||
public class OutdoorFairDeviceServiceImpl
|
||||
extends ServiceImpl<OutdoorFairDeviceMapper, OutdoorFairDevice>
|
||||
implements IOutdoorFairDeviceService
|
||||
{
|
||||
@Autowired
|
||||
private FairCompanyMapper fairCompanyMapper;
|
||||
|
||||
@Autowired
|
||||
private CompanyMapper companyMapper;
|
||||
|
||||
@Override
|
||||
public List<OutdoorFairDevice> selectDeviceList(OutdoorFairDevice query)
|
||||
{
|
||||
LambdaQueryWrapper<OutdoorFairDevice> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(query.getFairId() != null, OutdoorFairDevice::getFairId, query.getFairId());
|
||||
wrapper.eq(query.getCompanyId() != null, OutdoorFairDevice::getCompanyId, query.getCompanyId());
|
||||
wrapper.like(StringUtils.isNotBlank(query.getDeviceCode()), OutdoorFairDevice::getDeviceCode, query.getDeviceCode());
|
||||
wrapper.like(StringUtils.isNotBlank(query.getCompanyName()), OutdoorFairDevice::getCompanyName, query.getCompanyName());
|
||||
wrapper.orderByDesc(OutdoorFairDevice::getId);
|
||||
return list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OutdoorFairDevice> batchGenerate(Long fairId, Integer quantity)
|
||||
{
|
||||
if (fairId == null) {
|
||||
throw new ServiceException("招聘会ID不能为空");
|
||||
}
|
||||
int count = quantity == null ? 0 : quantity;
|
||||
if (count <= 0) {
|
||||
throw new ServiceException("生成数量必须大于0");
|
||||
}
|
||||
if (count > 1000) {
|
||||
throw new ServiceException("单次生成数量不能超过1000");
|
||||
}
|
||||
List<OutdoorFairDevice> devices = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
OutdoorFairDevice device = new OutdoorFairDevice();
|
||||
device.setFairId(fairId);
|
||||
// 去掉中划线,得到 32 位十六进制设备码
|
||||
device.setDeviceCode(UUID.randomUUID().toString().replace("-", ""));
|
||||
devices.add(device);
|
||||
}
|
||||
saveBatch(devices);
|
||||
return devices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindCompany(Long id, Long companyId)
|
||||
{
|
||||
OutdoorFairDevice device = getById(id);
|
||||
if (device == null) {
|
||||
throw new ServiceException("设备码不存在");
|
||||
}
|
||||
// 使用 UpdateWrapper 显式 set,避免 updateById 默认跳过 null 字段导致解绑失效
|
||||
UpdateWrapper<OutdoorFairDevice> wrapper = new UpdateWrapper<>();
|
||||
wrapper.eq("id", id);
|
||||
if (companyId != null) {
|
||||
// 仅允许绑定已参加当前招聘会的企业
|
||||
Long count = fairCompanyMapper.selectCount(new LambdaQueryWrapper<FairCompany>()
|
||||
.eq(FairCompany::getJobFairId, device.getFairId())
|
||||
.eq(FairCompany::getCompanyId, companyId));
|
||||
if (count == null || count == 0) {
|
||||
throw new ServiceException("该企业未参加当前招聘会");
|
||||
}
|
||||
// 同一招聘会下,一个企业只能绑定一个设备码
|
||||
Long bound = baseMapper.selectCount(new LambdaQueryWrapper<OutdoorFairDevice>()
|
||||
.eq(OutdoorFairDevice::getFairId, device.getFairId())
|
||||
.eq(OutdoorFairDevice::getCompanyId, companyId));
|
||||
if (bound != null && bound > 0) {
|
||||
throw new ServiceException("该企业已绑定其他设备码");
|
||||
}
|
||||
Company company = companyMapper.selectById(companyId);
|
||||
wrapper.set("company_id", companyId)
|
||||
.set("company_name", company == null ? null : company.getName())
|
||||
.set("bind_time", new Date());
|
||||
}
|
||||
else {
|
||||
// 解绑:显式置空
|
||||
wrapper.set("company_id", null)
|
||||
.set("company_name", null)
|
||||
.set("bind_time", null);
|
||||
}
|
||||
return baseMapper.update(null, wrapper) > 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user