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:
@@ -1,13 +1,18 @@
|
||||
---
|
||||
name: query-shz-highgo
|
||||
description: Query the SHZ HighGo Security Enterprise Edition database through its SSH gateway using a guarded read-only psql workflow. Use when Claude Code is working in the shz-backend project and needs to inspect database schemas, tables, columns, row counts, or application data; diagnose data-related behavior; or execute SELECT, WITH, SHOW, or EXPLAIN statements against the project's HighGo database.
|
||||
description: Run SQL against the SHZ HighGo Security Enterprise Edition database through its SSH gateway. Use when Claude Code is working in the shz-backend project and needs to inspect schemas, tables, columns, row counts, or application data; diagnose data-related behavior; or execute SELECT/WITH/SHOW/EXPLAIN. Also supports write operations — INSERT, UPDATE, DELETE, MERGE, CREATE, ALTER, DROP, TRUNCATE, GRANT, REVOKE — when the user explicitly asks to add, modify, delete, or otherwise change data or schema.
|
||||
---
|
||||
|
||||
# Query SHZ HighGo
|
||||
# SHZ HighGo SQL
|
||||
|
||||
Use the bundled script to run read-only SQL through the configured SSH gateway.
|
||||
Two bundled scripts run SQL through the configured SSH gateway:
|
||||
|
||||
## Query workflow
|
||||
- `scripts/query.sh` — **read-only**. Default for inspection and diagnostics.
|
||||
- `scripts/modify.sh` — **write-capable**. Use only when an actual change is intended and the user has asked for it.
|
||||
|
||||
Both set `search_path` to `shz,public`, apply a 30-second `statement_timeout`, reject multiple statements in one call, and base64-encode the SQL over the SSH channel. `query.sh` additionally enforces database-level `default_transaction_read_only=on` and accepts only `SELECT/WITH/SHOW/EXPLAIN`.
|
||||
|
||||
## Query workflow (read-only)
|
||||
|
||||
1. Form the narrowest useful query. Add a small `LIMIT` when inspecting rows.
|
||||
2. For unfamiliar tables, inspect `information_schema.columns` before selecting business data.
|
||||
@@ -19,7 +24,26 @@ Use the bundled script to run read-only SQL through the configured SSH gateway.
|
||||
|
||||
4. Summarize the result and distinguish returned facts from inferences.
|
||||
|
||||
The script accepts only statements beginning with `SELECT`, `WITH`, `SHOW`, or `EXPLAIN`, rejects multiple statements, sets `search_path` to `shz,public`, enforces database-level read-only mode, and applies a 30-second timeout. Never bypass these controls or perform writes unless the user explicitly requests a separate write-capable workflow.
|
||||
## Modify workflow (writes)
|
||||
|
||||
⚠️ These statements change data or schema on a production database. Confirm the exact statement with the user before running it, and prefer a `WHERE` clause or scoped target. Read unfamiliar tables with `query.sh` first.
|
||||
|
||||
1. Inspect the target with `query.sh` (columns, existing rows) so the statement is well-formed.
|
||||
2. Run the write:
|
||||
|
||||
```bash
|
||||
.claude/skills/query-shz-highgo/scripts/modify.sh "INSERT INTO shz.some_table (col) VALUES ('val')"
|
||||
.claude/skills/query-shz-highgo/scripts/modify.sh "UPDATE shz.some_table SET col = 'val' WHERE id = 1"
|
||||
.claude/skills/query-shz-highgo/scripts/modify.sh "DELETE FROM shz.some_table WHERE id = 1"
|
||||
.claude/skills/query-shz-highgo/scripts/modify.sh "CREATE TABLE shz.example (id bigint PRIMARY KEY)"
|
||||
```
|
||||
|
||||
3. Verify the effect with a follow-up `query.sh` `SELECT` and report rows affected.
|
||||
|
||||
Notes:
|
||||
- Only one statement per call. To run several, invoke the script multiple times.
|
||||
- Transactions across calls are NOT supported (each call is its own session). psql autocommits each statement.
|
||||
- `DROP`/`TRUNCATE`/`DELETE` without a `WHERE` are destructive — re-state the impact to the user and wait for explicit go-ahead.
|
||||
|
||||
## Schema discovery
|
||||
|
||||
|
||||
46
.claude/skills/query-shz-highgo/scripts/modify.sh
Executable file
46
.claude/skills/query-shz-highgo/scripts/modify.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Write-capable counterpart to query.sh. Runs a single DML/DDL statement
|
||||
# against the SHZ HighGo database through the SSH gateway. Read-only mode
|
||||
# is NOT applied — only use when an actual change is intended.
|
||||
|
||||
skill_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
password_file="${HIGHGO_PASSWORD_FILE:-$skill_dir/.password}"
|
||||
|
||||
ssh_host="root@124.243.245.42"
|
||||
psql_path="/opt/HighGo4.5.7-see/bin/psql"
|
||||
db_host="39.98.44.136"
|
||||
db_port="6023"
|
||||
db_name="highgo"
|
||||
db_user="sysdba"
|
||||
|
||||
if [[ ! -r "$password_file" ]]; then
|
||||
echo "HighGo password file is missing or unreadable: $password_file" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "Usage: $0 'INSERT ...' | 'UPDATE ...' | 'DELETE ...' | 'CREATE ...' | 'ALTER ...' | 'DROP ...'" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
sql="$*"
|
||||
|
||||
# Reject multiple statements — prevents a trailing destructive command from
|
||||
# being smuggled in after the intended one. A single optional trailing
|
||||
# semicolon is tolerated.
|
||||
sql_without_final_semicolon="${sql%;}"
|
||||
if [[ "$sql_without_final_semicolon" == *';'* ]]; then
|
||||
echo "Rejected: multiple SQL statements are not allowed. Run them one at a time." >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
password="$(<"$password_file")"
|
||||
sql_base64="$(printf '%s' "$sql" | base64 | tr -d '\n')"
|
||||
|
||||
{
|
||||
printf '%s\n' "$password"
|
||||
printf '%s\n' "$sql_base64"
|
||||
} | ssh -o BatchMode=yes -o ConnectTimeout=10 "$ssh_host" \
|
||||
"read -r PGPASSWORD; read -r SQL_BASE64; export PGPASSWORD; export PGOPTIONS='-c search_path=shz,public -c statement_timeout=30000'; SQL=\$(printf '%s' \"\$SQL_BASE64\" | base64 -d); exec '$psql_path' -X --no-psqlrc -h '$db_host' -p '$db_port' -U '$db_user' -d '$db_name' -v ON_ERROR_STOP=1 -P pager=off -c \"\$SQL\""
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
42
sql/cms_outdoor_fair_attendee.sql
Normal file
42
sql/cms_outdoor_fair_attendee.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- 户外招聘会签到人员表
|
||||
-- 依赖:shz.cms_outdoor_fair(fair_id 外键逻辑关联,不建物理外键)
|
||||
|
||||
CREATE SEQUENCE IF NOT EXISTS shz.cms_outdoor_fair_attendee_id_seq
|
||||
START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS shz.cms_outdoor_fair_attendee (
|
||||
id BIGINT NOT NULL DEFAULT nextval('shz.cms_outdoor_fair_attendee_id_seq'::regclass) PRIMARY KEY,
|
||||
fair_id BIGINT NOT NULL,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
address VARCHAR(255),
|
||||
check_in_time TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
del_flag CHAR(1) DEFAULT '0'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cms_outdoor_fair_attendee_fair
|
||||
ON shz.cms_outdoor_fair_attendee (fair_id);
|
||||
|
||||
COMMENT ON TABLE shz.cms_outdoor_fair_attendee IS '户外招聘会签到人员表';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.id IS '主键';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.fair_id IS '招聘会ID';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.name IS '人员姓名';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.phone IS '手机号';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.address IS '住址';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.check_in_time IS '签到时间';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_attendee.del_flag IS '删除标志(0存在 2删除)';
|
||||
|
||||
-- mock 5 条签到人员数据,归属 id=8 的招聘会
|
||||
INSERT INTO shz.cms_outdoor_fair_attendee
|
||||
(fair_id, name, phone, address, check_in_time, create_by, create_time, del_flag)
|
||||
VALUES
|
||||
(8, '张伟', '13800000001', '石河子市东城街道北一路上段', '2026-06-25 09:05:00', 'admin', now(), '0'),
|
||||
(8, '李娜', '13900000002', '石河子市新城街道西一路社区', '2026-06-25 09:12:00', 'admin', now(), '0'),
|
||||
(8, '王强', '13700000003', '石河子市红山街道三小区', '2026-06-25 09:20:00', 'admin', now(), '0'),
|
||||
(8, '陈静', '13600000004', '石河子市向阳街道一社区', '2026-06-25 09:31:00', 'admin', now(), '0'),
|
||||
(8, '刘洋', '13500000005', '石河子市老街街道五小区', '2026-06-25 09:45:00', 'admin', now(), '0');
|
||||
42
sql/cms_outdoor_fair_device.sql
Normal file
42
sql/cms_outdoor_fair_device.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- 户外招聘会设备码表
|
||||
-- 依赖:shz.cms_outdoor_fair(fair_id 外键逻辑关联,不建物理外键)
|
||||
|
||||
CREATE SEQUENCE IF NOT EXISTS shz.cms_outdoor_fair_device_id_seq
|
||||
START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS shz.cms_outdoor_fair_device (
|
||||
id BIGINT NOT NULL DEFAULT nextval('shz.cms_outdoor_fair_device_id_seq'::regclass) PRIMARY KEY,
|
||||
fair_id BIGINT NOT NULL,
|
||||
device_code VARCHAR(64) NOT NULL,
|
||||
company_id BIGINT,
|
||||
company_name VARCHAR(255),
|
||||
bind_time TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
del_flag CHAR(1) DEFAULT '0'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cms_outdoor_fair_device_fair
|
||||
ON shz.cms_outdoor_fair_device (fair_id);
|
||||
|
||||
COMMENT ON TABLE shz.cms_outdoor_fair_device IS '户外招聘会设备码表';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.id IS '主键';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.fair_id IS '招聘会ID';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.device_code IS '设备码(UUID)';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.company_id IS '绑定的参会企业ID';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.company_name IS '绑定的参会企业名称';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.bind_time IS '绑定时间';
|
||||
COMMENT ON COLUMN shz.cms_outdoor_fair_device.del_flag IS '删除标志(0存在 2删除)';
|
||||
|
||||
-- mock 数据:为 id=8 的招聘会生成 5 个设备码,其中 2 个绑定参会企业
|
||||
INSERT INTO shz.cms_outdoor_fair_device
|
||||
(fair_id, device_code, company_id, company_name, bind_time, create_by, create_time, del_flag)
|
||||
VALUES
|
||||
(8, 'a1b2c3d4e5f6471a9b8c1d2e3f4a5b6c', null, null, null, 'admin', now(), '0'),
|
||||
(8, 'b2c3d4e5f6a7482bac9d0e1f2a3b4c5d', null, null, null, 'admin', now(), '0'),
|
||||
(8, 'c3d4e5f6a7b8493cbd0e1f2a3b4c5d6e', 101, '示例科技有限公司', '2026-06-25 10:00:00', 'admin', now(), '0'),
|
||||
(8, 'd4e5f6a7b8c9404dce1f2a3b4c5d6e7f', 102, '示范商贸有限公司', '2026-06-25 10:05:00', 'admin', now(), '0'),
|
||||
(8, 'e5f6a7b8c9d0415edf2a3b4c5d6e7f8a', null, null, null, 'admin', now(), '0');
|
||||
Reference in New Issue
Block a user