wuzhen update招生就业
This commit is contained in:
1 parent
7e122d76dd
commit
e5c6fd7a24
55 files changed
+3563
-2397
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -110,6 +110,20 @@
|
||||
<artifactId>ruoyi-xg</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- iText PDF生成库(用于招聘简章PDF生成) -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.13.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- iText亚洲字体支持包 - 支持中文 -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext-asian</artifactId>
|
||||
<version>5.2.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+165
-20
@@ -1,14 +1,20 @@
|
||||
package org.dromara.zsjy.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
@@ -20,15 +26,21 @@ import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseBrochureVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseImportVo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseBo;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseService;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseBrochureService;
|
||||
import org.dromara.zsjy.listener.ZsjyEnterpriseImportListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 企业信息
|
||||
* 企业信息管理Controller
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author Lion Li (改造: Cursor AI 2026-03-30)
|
||||
* @date 2023-07-05
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@@ -36,11 +48,12 @@ import org.dromara.zsjy.service.IZsjyEnterpriseService;
|
||||
public class ZsjyEnterpriseController extends BaseController {
|
||||
|
||||
private final IZsjyEnterpriseService enterpriseService;
|
||||
private final IZsjyEnterpriseBrochureService brochureService;
|
||||
|
||||
/**
|
||||
* 查询企业信息列表
|
||||
* 查询企业信息列表(包含岗位数量)
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:list")
|
||||
@SaCheckPermission("zsjy:enterprise:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ZsjyEnterpriseVo> list(ZsjyEnterpriseBo bo, PageQuery pageQuery) {
|
||||
return enterpriseService.queryPageList(bo, pageQuery);
|
||||
@@ -49,8 +62,8 @@ public class ZsjyEnterpriseController extends BaseController {
|
||||
/**
|
||||
* 导出企业信息列表
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:export")
|
||||
@Log(title = "企业信息", businessType = BusinessType.EXPORT)
|
||||
@SaCheckPermission("zsjy:enterprise:export")
|
||||
@PostMapping("/export")
|
||||
public void export(ZsjyEnterpriseBo bo, HttpServletResponse response) {
|
||||
List<ZsjyEnterpriseVo> list = enterpriseService.queryList(bo);
|
||||
@@ -58,21 +71,19 @@ public class ZsjyEnterpriseController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业信息详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
* 获取企业信息详细信息(包含岗位列表)
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:query")
|
||||
@SaCheckPermission("zsjy:enterprise:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ZsjyEnterpriseVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(enterpriseService.queryById(id));
|
||||
public R<ZsjyEnterpriseVo> getInfo(@PathVariable Long id) {
|
||||
return R.ok(enterpriseService.queryWithJobs(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增企业信息
|
||||
* 新增企业信息(同时新增岗位列表)
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:add")
|
||||
@Log(title = "企业信息", businessType = BusinessType.INSERT)
|
||||
@SaCheckPermission("zsjy:enterprise:add")
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ZsjyEnterpriseBo bo) {
|
||||
@@ -80,10 +91,10 @@ public class ZsjyEnterpriseController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改企业信息
|
||||
* 修改企业信息(同时更新岗位列表)
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:edit")
|
||||
@Log(title = "企业信息", businessType = BusinessType.UPDATE)
|
||||
@SaCheckPermission("zsjy:enterprise:edit")
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ZsjyEnterpriseBo bo) {
|
||||
@@ -92,22 +103,156 @@ public class ZsjyEnterpriseController extends BaseController {
|
||||
|
||||
/**
|
||||
* 删除企业信息
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:remove")
|
||||
@Log(title = "企业信息", businessType = BusinessType.DELETE)
|
||||
@SaCheckPermission("zsjy:enterprise:remove")
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
public R<Void> remove(@PathVariable Long[] ids) {
|
||||
return toAjax(enterpriseService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业下拉选项
|
||||
*/
|
||||
// @SaCheckPermission("zsjy:enterprise:list")
|
||||
@GetMapping("/options")
|
||||
public R<List<ZsjyEnterpriseVo>> getOptions() {
|
||||
return R.ok(enterpriseService.queryList(new ZsjyEnterpriseBo()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载导入模板
|
||||
*/
|
||||
@GetMapping("/importTemplate")
|
||||
public void importTemplate(HttpServletResponse response) {
|
||||
ExcelUtil.exportExcel(new ArrayList<>(List.of(new ZsjyEnterpriseImportVo())), "企业信息导入模板", ZsjyEnterpriseImportVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入企业信息(含岗位)
|
||||
*
|
||||
* @param file Excel文件
|
||||
* @param updateSupport 是否支持更新(同名覆盖)
|
||||
*/
|
||||
@Log(title = "企业信息", businessType = BusinessType.IMPORT)
|
||||
@SaCheckPermission("zsjy:enterprise:import")
|
||||
@PostMapping("/importData")
|
||||
@RepeatSubmit()
|
||||
public R<String> importData(MultipartFile file, @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
|
||||
String resultMsg = ExcelUtil.importExcel(file.getInputStream(), ZsjyEnterpriseImportVo.class, new ZsjyEnterpriseImportListener(updateSupport)).getAnalysis();
|
||||
return R.ok(resultMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载企业招聘简章PDF(文件名使用企业名称)
|
||||
*/
|
||||
@SaCheckPermission("zsjy:enterprise:brochure")
|
||||
@GetMapping("/brochure/download/{enterpriseId}")
|
||||
public void downloadBrochure(@PathVariable Long enterpriseId, HttpServletResponse response) {
|
||||
try {
|
||||
// 优先从简章记录获取企业名称(更可靠)
|
||||
ZsjyEnterpriseBrochureVo brochure = brochureService.getLatestBrochure(enterpriseId);
|
||||
String fileName = null;
|
||||
if (brochure != null && StringUtils.isNotBlank(brochure.getEnterpriseName())) {
|
||||
fileName = brochure.getEnterpriseName();
|
||||
}
|
||||
// 如果简章记录中没有企业名称,再查询企业信息
|
||||
if (fileName == null) {
|
||||
ZsjyEnterpriseVo enterprise = enterpriseService.queryById(enterpriseId);
|
||||
if (enterprise != null) {
|
||||
fileName = enterprise.getEnterpriseName();
|
||||
}
|
||||
}
|
||||
brochureService.downloadPdf(enterpriseId, fileName, response);
|
||||
} catch (ServiceException e) {
|
||||
writeErrorResponse(response, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("下载PDF异常,企业ID: {}", enterpriseId, e);
|
||||
writeErrorResponse(response, "下载PDF失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新生成的简章信息
|
||||
*/
|
||||
@SaCheckPermission("zsjy:enterprise:query")
|
||||
@GetMapping("/brochure/{enterpriseId}")
|
||||
public R<ZsjyEnterpriseBrochureVo> getBrochure(@PathVariable Long enterpriseId) {
|
||||
return R.ok(brochureService.getLatestBrochure(enterpriseId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成招聘简章PDF(单个)
|
||||
*/
|
||||
@Log(title = "企业招聘简章", businessType = BusinessType.INSERT)
|
||||
@SaCheckPermission("zsjy:enterprise:brochure")
|
||||
@PostMapping("/brochure/{enterpriseId}")
|
||||
public R<ZsjyEnterpriseBrochureVo> generateBrochure(@PathVariable Long enterpriseId) {
|
||||
return R.ok(brochureService.generateBrochure(enterpriseId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成招聘简章PDF
|
||||
*
|
||||
* @param enterpriseIds 企业ID列表(多个用逗号分隔)
|
||||
*/
|
||||
@Log(title = "批量生成企业招聘简章", businessType = BusinessType.INSERT)
|
||||
@SaCheckPermission("zsjy:enterprise:brochure")
|
||||
@PostMapping("/brochure/batch/{enterpriseIds}")
|
||||
public R<List<ZsjyEnterpriseBrochureVo>> generateBrochureBatch(@PathVariable String enterpriseIds) {
|
||||
List<Long> ids = Arrays.stream(enterpriseIds.split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.map(Long::parseLong)
|
||||
.collect(Collectors.toList());
|
||||
return R.ok(brochureService.generateBrochureBatch(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下载多页PDF(每个企业一页)
|
||||
*
|
||||
* @param enterpriseIds 企业ID列表(多个用逗号分隔)
|
||||
*/
|
||||
@SaCheckPermission("zsjy:enterprise:brochure")
|
||||
@PostMapping("/brochure/batch/download")
|
||||
public void downloadBatchBrochure(@RequestBody java.util.List<Long> enterpriseIds, HttpServletResponse response) {
|
||||
try {
|
||||
brochureService.downloadBatchPdf(enterpriseIds, response);
|
||||
} catch (ServiceException e) {
|
||||
writeErrorResponse(response, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("批量下载PDF异常", e);
|
||||
writeErrorResponse(response, "批量下载PDF失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载简章PDF(通过简章记录ID)
|
||||
*/
|
||||
@SaCheckPermission("zsjy:enterprise:brochure")
|
||||
@GetMapping("/brochure/downloadById/{id}")
|
||||
public void downloadBrochureById(@PathVariable Long id, HttpServletResponse response) {
|
||||
try {
|
||||
brochureService.downloadPdfById(id, response);
|
||||
} catch (ServiceException e) {
|
||||
writeErrorResponse(response, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("下载PDF异常,简章ID: {}", id, e);
|
||||
writeErrorResponse(response, "下载PDF失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接写出错误响应(避免全局异常处理器返回JSON导致PDF损坏)
|
||||
*/
|
||||
private void writeErrorResponse(HttpServletResponse response, String message) {
|
||||
try {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.getWriter().write("{\"code\":500,\"msg\":\"" + message + "\",\"data\":null}");
|
||||
response.getWriter().flush();
|
||||
} catch (IOException e) {
|
||||
log.error("写出错误响应失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -46,6 +46,11 @@ public class ZsjyEnterprise extends TenantEntity implements Serializable {
|
||||
*/
|
||||
private String enterpriseScale;
|
||||
|
||||
/**
|
||||
* 企业简介(≤600字,用于招聘简章)
|
||||
*/
|
||||
private String enterpriseIntro;
|
||||
|
||||
/**
|
||||
* 企业联系人姓名
|
||||
*/
|
||||
@@ -56,11 +61,21 @@ public class ZsjyEnterprise extends TenantEntity implements Serializable {
|
||||
*/
|
||||
private String contactPhone;
|
||||
|
||||
/**
|
||||
* 办公电话
|
||||
*/
|
||||
private String officePhone;
|
||||
|
||||
/**
|
||||
* 企业邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 企业官网
|
||||
*/
|
||||
private String website;
|
||||
|
||||
/**
|
||||
* 企业地址
|
||||
*/
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package org.dromara.zsjy.domain;
|
||||
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 简章生成记录对象 zsjy_enterprise_brochure
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Data
|
||||
@TableName("zsjy_enterprise_brochure")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ZsjyEnterpriseBrochure extends TenantEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 企业ID
|
||||
*/
|
||||
private Long enterpriseId;
|
||||
|
||||
/**
|
||||
* PDF存储路径(OSS)
|
||||
*/
|
||||
private String brochurePath;
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 生成人
|
||||
*/
|
||||
private Long generateBy;
|
||||
|
||||
/**
|
||||
* 生成人姓名
|
||||
*/
|
||||
private String generateByName;
|
||||
|
||||
/**
|
||||
* 生成时间
|
||||
*/
|
||||
private java.util.Date generateTime;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package org.dromara.zsjy.domain;
|
||||
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业招聘岗位对象 zsjy_enterprise_job
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Data
|
||||
@TableName("zsjy_enterprise_job")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ZsjyEnterpriseJob extends TenantEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 岗位ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 所属企业ID
|
||||
*/
|
||||
private Long enterpriseId;
|
||||
|
||||
/**
|
||||
* 岗位名称
|
||||
*/
|
||||
private String positionName;
|
||||
|
||||
/**
|
||||
* 岗位要求
|
||||
*/
|
||||
private String requirements;
|
||||
|
||||
/**
|
||||
* 待遇
|
||||
*/
|
||||
private String benefits;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
}
|
||||
+34
-15
@@ -7,12 +7,16 @@ import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyEnterprise;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseJob;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业信息业务对象 zsjy_enterprise
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author Lion Li (改造: Cursor AI 2026-03-30)
|
||||
* @date 2023-07-05
|
||||
*/
|
||||
@Data
|
||||
@@ -30,35 +34,35 @@ public class ZsjyEnterpriseBo extends BaseEntity {
|
||||
* 企业名称
|
||||
*/
|
||||
@NotBlank(message = "企业名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 100, message = "企业名称长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 200, message = "企业名称长度不能超过200个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String enterpriseName;
|
||||
|
||||
/**
|
||||
* 所属行业
|
||||
*/
|
||||
@NotBlank(message = "所属行业不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 50, message = "所属行业长度不能超过50个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String industry;
|
||||
|
||||
/**
|
||||
* 主营业务
|
||||
* 主营业务(废弃字段,兼容保留)
|
||||
*/
|
||||
@NotBlank(message = "主营业务不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 500, message = "主营业务长度不能超过500个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String mainBusiness;
|
||||
|
||||
/**
|
||||
* 企业规模
|
||||
*/
|
||||
@NotBlank(message = "企业规模不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 20, message = "企业规模长度不能超过20个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String enterpriseScale;
|
||||
|
||||
/**
|
||||
* 企业简介(≤600字)
|
||||
*/
|
||||
@Size(max = 1500, message = "企业简介长度不能超过1500个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String enterpriseIntro;
|
||||
|
||||
/**
|
||||
* 企业联系人姓名
|
||||
*/
|
||||
@NotBlank(message = "企业联系人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 50, message = "企业联系人姓名长度不能超过50个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
@NotBlank(message = "联系人姓名不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 50, message = "联系人姓名长度不能超过50个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String contactName;
|
||||
|
||||
/**
|
||||
@@ -68,14 +72,24 @@ public class ZsjyEnterpriseBo extends BaseEntity {
|
||||
@Size(min = 1, max = 20, message = "联系电话长度不能超过20个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String contactPhone;
|
||||
|
||||
/**
|
||||
* 办公电话
|
||||
*/
|
||||
private String officePhone;
|
||||
|
||||
/**
|
||||
* 企业邮箱
|
||||
*/
|
||||
@NotBlank(message = "企业邮箱不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Email(message = "企业邮箱格式不正确", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 100, message = "企业邮箱长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
@NotBlank(message = "电子邮箱不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Email(message = "电子邮箱格式不正确", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 100, message = "电子邮箱长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 企业官网
|
||||
*/
|
||||
private String website;
|
||||
|
||||
/**
|
||||
* 企业地址
|
||||
*/
|
||||
@@ -89,7 +103,12 @@ public class ZsjyEnterpriseBo extends BaseEntity {
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
* 备注(废弃字段,兼容保留)
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 岗位列表(用于新增/编辑时传递岗位信息)
|
||||
*/
|
||||
private List<ZsjyEnterpriseJobBo> jobList;
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package org.dromara.zsjy.domain.bo;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseJob;
|
||||
|
||||
/**
|
||||
* 企业招聘岗位业务对象 zsjy_enterprise_job
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ZsjyEnterpriseJob.class, reverseConvertGenerate = false)
|
||||
public class ZsjyEnterpriseJobBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 岗位ID
|
||||
*/
|
||||
@NotNull(message = "岗位ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 所属企业ID
|
||||
*/
|
||||
@NotNull(message = "所属企业ID不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long enterpriseId;
|
||||
|
||||
/**
|
||||
* 岗位名称
|
||||
*/
|
||||
@NotBlank(message = "岗位名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(min = 1, max = 100, message = "岗位名称长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String positionName;
|
||||
|
||||
/**
|
||||
* 岗位要求
|
||||
*/
|
||||
private String requirements;
|
||||
|
||||
/**
|
||||
* 待遇
|
||||
*/
|
||||
private String benefits;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package org.dromara.zsjy.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseBrochure;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 简章生成记录视图对象
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = ZsjyEnterpriseBrochure.class)
|
||||
public class ZsjyEnterpriseBrochureVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 企业ID
|
||||
*/
|
||||
private Long enterpriseId;
|
||||
|
||||
/**
|
||||
* 企业名称(关联字段)
|
||||
*/
|
||||
private String enterpriseName;
|
||||
|
||||
/**
|
||||
* PDF存储路径(OSS)
|
||||
*/
|
||||
private String brochurePath;
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 生成人
|
||||
*/
|
||||
private Long generateBy;
|
||||
|
||||
/**
|
||||
* 生成人姓名
|
||||
*/
|
||||
private String generateByName;
|
||||
|
||||
/**
|
||||
* 生成时间
|
||||
*/
|
||||
private Date generateTime;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package org.dromara.zsjy.domain.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业信息导入VO(支持企业+最多6个岗位)
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Data
|
||||
@cn.idev.excel.annotation.ExcelIgnoreUnannotated
|
||||
public class ZsjyEnterpriseImportVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/* ==================== 企业基本信息 ==================== */
|
||||
|
||||
/**
|
||||
* 企业名称(必填)
|
||||
*/
|
||||
@ExcelProperty("企业名称")
|
||||
@NotBlank(message = "企业名称不能为空")
|
||||
@Size(max = 200, message = "企业名称长度不能超过200")
|
||||
private String enterpriseName;
|
||||
|
||||
/**
|
||||
* 企业简介(选填,最多600字)
|
||||
*/
|
||||
@ExcelProperty("企业简介")
|
||||
@Size(max = 1500, message = "企业简介长度不能超过1500字符")
|
||||
private String enterpriseIntro;
|
||||
|
||||
/**
|
||||
* 企业地址(必填)
|
||||
*/
|
||||
@ExcelProperty("企业地址")
|
||||
@NotBlank(message = "企业地址不能为空")
|
||||
@Size(max = 255, message = "企业地址长度不能超过255")
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 联系人(必填)
|
||||
*/
|
||||
@ExcelProperty("联系人")
|
||||
@NotBlank(message = "联系人不能为空")
|
||||
@Size(max = 50, message = "联系人姓名长度不能超过50")
|
||||
private String contactName;
|
||||
|
||||
/**
|
||||
* 联系电话(必填,手机号)
|
||||
*/
|
||||
@ExcelProperty("联系电话")
|
||||
@NotBlank(message = "联系电话不能为空")
|
||||
@Size(max = 20, message = "联系电话长度不能超过20")
|
||||
private String contactPhone;
|
||||
|
||||
/**
|
||||
* 办公电话(选填)
|
||||
*/
|
||||
@ExcelProperty("办公电话")
|
||||
@Size(max = 20, message = "办公电话长度不能超过20")
|
||||
private String officePhone;
|
||||
|
||||
/**
|
||||
* 电子邮箱(必填)
|
||||
*/
|
||||
@ExcelProperty("电子邮箱")
|
||||
@NotBlank(message = "电子邮箱不能为空")
|
||||
@Email(message = "电子邮箱格式不正确")
|
||||
@Size(max = 100, message = "电子邮箱长度不能超过100")
|
||||
private String email;
|
||||
|
||||
/* ==================== 岗位1 ==================== */
|
||||
|
||||
/**
|
||||
* 招聘岗位1-岗位名称
|
||||
*/
|
||||
@ExcelProperty("招聘岗位1")
|
||||
@Size(max = 100, message = "岗位名称长度不能超过100")
|
||||
private String positionName1;
|
||||
|
||||
/**
|
||||
* 招聘岗位1-要求
|
||||
*/
|
||||
@ExcelProperty("要求1")
|
||||
private String requirements1;
|
||||
|
||||
/**
|
||||
* 招聘岗位1-待遇
|
||||
*/
|
||||
@ExcelProperty("待遇1")
|
||||
private String benefits1;
|
||||
|
||||
/* ==================== 岗位2 ==================== */
|
||||
|
||||
@ExcelProperty("招聘岗位2")
|
||||
@Size(max = 100, message = "岗位名称长度不能超过100")
|
||||
private String positionName2;
|
||||
|
||||
@ExcelProperty("要求2")
|
||||
private String requirements2;
|
||||
|
||||
@ExcelProperty("待遇2")
|
||||
private String benefits2;
|
||||
|
||||
/* ==================== 岗位3 ==================== */
|
||||
|
||||
@ExcelProperty("招聘岗位3")
|
||||
@Size(max = 100, message = "岗位名称长度不能超过100")
|
||||
private String positionName3;
|
||||
|
||||
@ExcelProperty("要求3")
|
||||
private String requirements3;
|
||||
|
||||
@ExcelProperty("待遇3")
|
||||
private String benefits3;
|
||||
|
||||
/* ==================== 岗位4 ==================== */
|
||||
|
||||
@ExcelProperty("招聘岗位4")
|
||||
@Size(max = 100, message = "岗位名称长度不能超过100")
|
||||
private String positionName4;
|
||||
|
||||
@ExcelProperty("要求4")
|
||||
private String requirements4;
|
||||
|
||||
@ExcelProperty("待遇4")
|
||||
private String benefits4;
|
||||
|
||||
/* ==================== 岗位5 ==================== */
|
||||
|
||||
@ExcelProperty("招聘岗位5")
|
||||
@Size(max = 100, message = "岗位名称长度不能超过100")
|
||||
private String positionName5;
|
||||
|
||||
@ExcelProperty("要求5")
|
||||
private String requirements5;
|
||||
|
||||
@ExcelProperty("待遇5")
|
||||
private String benefits5;
|
||||
|
||||
/* ==================== 岗位6 ==================== */
|
||||
|
||||
@ExcelProperty("招聘岗位6")
|
||||
@Size(max = 100, message = "岗位名称长度不能超过100")
|
||||
private String positionName6;
|
||||
|
||||
@ExcelProperty("要求6")
|
||||
private String requirements6;
|
||||
|
||||
@ExcelProperty("待遇6")
|
||||
private String benefits6;
|
||||
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.dromara.zsjy.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseJob;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 企业招聘岗位视图对象
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = ZsjyEnterpriseJob.class)
|
||||
public class ZsjyEnterpriseJobVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 岗位ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 所属企业ID
|
||||
*/
|
||||
private Long enterpriseId;
|
||||
|
||||
/**
|
||||
* 岗位名称
|
||||
*/
|
||||
private String positionName;
|
||||
|
||||
/**
|
||||
* 岗位要求
|
||||
*/
|
||||
private String requirements;
|
||||
|
||||
/**
|
||||
* 待遇
|
||||
*/
|
||||
private String benefits;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
private Integer deleted;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 创建部门
|
||||
*/
|
||||
private Long createDept;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
}
|
||||
+30
-4
@@ -6,11 +6,12 @@ import org.dromara.zsjy.domain.ZsjyEnterprise;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业信息视图对象
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author Lion Li (改造: Cursor AI 2026-03-30)
|
||||
* @date 2023-07-05
|
||||
*/
|
||||
@Data
|
||||
@@ -40,7 +41,7 @@ public class ZsjyEnterpriseVo implements Serializable {
|
||||
private String industryName;
|
||||
|
||||
/**
|
||||
* 主营业务
|
||||
* 主营业务(废弃字段,仅保留兼容)
|
||||
*/
|
||||
private String mainBusiness;
|
||||
|
||||
@@ -54,6 +55,11 @@ public class ZsjyEnterpriseVo implements Serializable {
|
||||
*/
|
||||
private String enterpriseScaleName;
|
||||
|
||||
/**
|
||||
* 企业简介(≤600字,用于招聘简章)
|
||||
*/
|
||||
private String enterpriseIntro;
|
||||
|
||||
/**
|
||||
* 企业联系人姓名
|
||||
*/
|
||||
@@ -65,10 +71,20 @@ public class ZsjyEnterpriseVo implements Serializable {
|
||||
private String contactPhone;
|
||||
|
||||
/**
|
||||
* 企业邮箱
|
||||
* 办公电话
|
||||
*/
|
||||
private String officePhone;
|
||||
|
||||
/**
|
||||
* 电子邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 企业官网
|
||||
*/
|
||||
private String website;
|
||||
|
||||
/**
|
||||
* 企业地址
|
||||
*/
|
||||
@@ -80,7 +96,7 @@ public class ZsjyEnterpriseVo implements Serializable {
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
* 备注(废弃字段,仅保留兼容)
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
@@ -118,4 +134,14 @@ public class ZsjyEnterpriseVo implements Serializable {
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 岗位列表(用于招聘简章)
|
||||
*/
|
||||
private List<ZsjyEnterpriseJobVo> jobList;
|
||||
|
||||
/**
|
||||
* 岗位数量(列表展示用)
|
||||
*/
|
||||
private Integer jobCount;
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package org.dromara.zsjy.listener;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import cn.idev.excel.context.AnalysisContext;
|
||||
import cn.idev.excel.event.AnalysisEventListener;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.excel.core.ExcelListener;
|
||||
import org.dromara.common.excel.core.ExcelResult;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseBo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseJobBo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseImportVo;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业信息导入Listener
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZsjyEnterpriseImportListener extends AnalysisEventListener<ZsjyEnterpriseImportVo>
|
||||
implements ExcelListener<ZsjyEnterpriseImportVo> {
|
||||
|
||||
private final Boolean isUpdateSupport;
|
||||
private final String operName;
|
||||
private final Long operUserId;
|
||||
|
||||
private int successNum = 0;
|
||||
private int failureNum = 0;
|
||||
private final StringBuilder successMsg = new StringBuilder();
|
||||
private final StringBuilder failureMsg = new StringBuilder();
|
||||
|
||||
public ZsjyEnterpriseImportListener(Boolean isUpdateSupport) {
|
||||
this.isUpdateSupport = isUpdateSupport;
|
||||
this.operName = LoginHelper.getUsername();
|
||||
this.operUserId = LoginHelper.getUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(ZsjyEnterpriseImportVo importVo, AnalysisContext context) {
|
||||
try {
|
||||
if (importVo == null) {
|
||||
failureNum++;
|
||||
failureMsg.append("<br/>").append(failureNum).append("、第").append(context.readRowHolder().getRowIndex() + 1)
|
||||
.append("行:数据为空");
|
||||
return;
|
||||
}
|
||||
// 调试日志
|
||||
log.info("导入第{}行数据: enterpriseName={}, address={}, contactName={}",
|
||||
context.readRowHolder().getRowIndex() + 1,
|
||||
importVo.getEnterpriseName(),
|
||||
importVo.getAddress(),
|
||||
importVo.getContactName());
|
||||
|
||||
ZsjyEnterpriseBo bo = convertToBo(importVo);
|
||||
log.info("转换后BO: enterpriseName={}", bo.getEnterpriseName());
|
||||
|
||||
IZsjyEnterpriseService enterpriseService = SpringUtils.getBean(IZsjyEnterpriseService.class);
|
||||
enterpriseService.importData(List.of(bo), isUpdateSupport, operName);
|
||||
successNum++;
|
||||
successMsg.append("<br/>").append(successNum).append("、企业 ").append(bo.getEnterpriseName())
|
||||
.append(" 导入成功");
|
||||
} catch (Exception e) {
|
||||
failureNum++;
|
||||
// 安全处理null情况
|
||||
String enterpriseName = importVo.getEnterpriseName() != null ? importVo.getEnterpriseName() : "未知企业";
|
||||
String msg = "<br/>" + failureNum + "、第" + (context.readRowHolder().getRowIndex() + 1)
|
||||
+ "行 企业 " + HtmlUtil.cleanHtmlTag(enterpriseName) + " 导入失败:" + e.getMessage();
|
||||
failureMsg.append(msg);
|
||||
log.error(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ZsjyEnterpriseBo convertToBo(ZsjyEnterpriseImportVo vo) {
|
||||
ZsjyEnterpriseBo bo = new ZsjyEnterpriseBo();
|
||||
bo.setEnterpriseName(vo.getEnterpriseName());
|
||||
bo.setEnterpriseIntro(vo.getEnterpriseIntro());
|
||||
bo.setAddress(vo.getAddress());
|
||||
bo.setContactName(vo.getContactName());
|
||||
bo.setContactPhone(vo.getContactPhone());
|
||||
bo.setOfficePhone(vo.getOfficePhone());
|
||||
bo.setEmail(vo.getEmail());
|
||||
bo.setStatus("0");
|
||||
|
||||
List<ZsjyEnterpriseJobBo> jobs = new ArrayList<>();
|
||||
addJobIfPresent(jobs, vo.getPositionName1(), vo.getRequirements1(), vo.getBenefits1());
|
||||
addJobIfPresent(jobs, vo.getPositionName2(), vo.getRequirements2(), vo.getBenefits2());
|
||||
addJobIfPresent(jobs, vo.getPositionName3(), vo.getRequirements3(), vo.getBenefits3());
|
||||
addJobIfPresent(jobs, vo.getPositionName4(), vo.getRequirements4(), vo.getBenefits4());
|
||||
addJobIfPresent(jobs, vo.getPositionName5(), vo.getRequirements5(), vo.getBenefits5());
|
||||
addJobIfPresent(jobs, vo.getPositionName6(), vo.getRequirements6(), vo.getBenefits6());
|
||||
bo.setJobList(jobs);
|
||||
return bo;
|
||||
}
|
||||
|
||||
private void addJobIfPresent(List<ZsjyEnterpriseJobBo> jobs, String name, String requirements, String benefits) {
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
ZsjyEnterpriseJobBo job = new ZsjyEnterpriseJobBo();
|
||||
job.setPositionName(name);
|
||||
job.setRequirements(requirements);
|
||||
job.setBenefits(benefits);
|
||||
job.setStatus("0");
|
||||
jobs.add(job);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExcelResult<ZsjyEnterpriseImportVo> getExcelResult() {
|
||||
return new ExcelResult<>() {
|
||||
@Override
|
||||
public String getAnalysis() {
|
||||
if (failureNum > 0) {
|
||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
||||
throw new ServiceException(failureMsg.toString());
|
||||
} else {
|
||||
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
|
||||
}
|
||||
return successMsg.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZsjyEnterpriseImportVo> getList() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getErrorList() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.dromara.zsjy.mapper;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseBrochure;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseBrochureVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 简章生成记录Mapper接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
public interface ZsjyEnterpriseBrochureMapper extends BaseMapperPlus<ZsjyEnterpriseBrochure, ZsjyEnterpriseBrochureVo> {
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.dromara.zsjy.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseJob;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 企业招聘岗位Mapper接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
public interface ZsjyEnterpriseJobMapper extends BaseMapperPlus<ZsjyEnterpriseJob, ZsjyEnterpriseJobVo> {
|
||||
|
||||
/**
|
||||
* 按企业ID查询岗位列表(用于简章展示)
|
||||
*/
|
||||
List<ZsjyEnterpriseJobVo> selectVoListByEnterpriseId(Long enterpriseId);
|
||||
|
||||
}
|
||||
+23
@@ -2,7 +2,13 @@ package org.dromara.zsjy.mapper;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyEnterprise;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseBo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 企业信息 Mapper 接口
|
||||
@@ -12,4 +18,21 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
*/
|
||||
public interface ZsjyEnterpriseMapper extends BaseMapperPlus<ZsjyEnterprise, ZsjyEnterpriseVo> {
|
||||
|
||||
/**
|
||||
* 查询企业详情(含岗位列表,使用XML自定义resultMap)
|
||||
*
|
||||
* @param id 企业ID
|
||||
* @return 企业信息(含岗位列表)
|
||||
*/
|
||||
ZsjyEnterpriseVo selectVoWithJobsById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询企业列表(包含岗位数量,job_count通过子查询计算)
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param bo 查询条件
|
||||
* @return 企业分页列表
|
||||
*/
|
||||
Page<ZsjyEnterpriseVo> selectVoPageWithJobCount(IPage<ZsjyEnterpriseVo> page, @Param("bo") ZsjyEnterpriseBo bo);
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package org.dromara.zsjy.service;
|
||||
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseBrochureVo;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 招聘简章生成Service接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
public interface IZsjyEnterpriseBrochureService {
|
||||
|
||||
/**
|
||||
* 生成企业招聘简章PDF
|
||||
*
|
||||
* @param enterpriseId 企业ID
|
||||
* @return 简章记录
|
||||
*/
|
||||
ZsjyEnterpriseBrochureVo generateBrochure(Long enterpriseId);
|
||||
|
||||
/**
|
||||
* 批量生成企业招聘简章PDF
|
||||
*
|
||||
* @param enterpriseIds 企业ID列表
|
||||
* @return 简章记录列表
|
||||
*/
|
||||
List<ZsjyEnterpriseBrochureVo> generateBrochureBatch(List<Long> enterpriseIds);
|
||||
|
||||
/**
|
||||
* 下载PDF(通过response直接输出)
|
||||
*
|
||||
* @param enterpriseId 企业ID
|
||||
* @param response HttpServletResponse
|
||||
*/
|
||||
void downloadPdf(Long enterpriseId, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 下载PDF(支持自定义文件名)
|
||||
*
|
||||
* @param enterpriseId 企业ID
|
||||
* @param fileName 自定义文件名(不含扩展名)
|
||||
* @param response HttpServletResponse
|
||||
*/
|
||||
void downloadPdf(Long enterpriseId, String fileName, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 获取简章记录
|
||||
*
|
||||
* @param enterpriseId 企业ID
|
||||
* @return 简章记录
|
||||
*/
|
||||
ZsjyEnterpriseBrochureVo getLatestBrochure(Long enterpriseId);
|
||||
|
||||
/**
|
||||
* 按ID下载PDF
|
||||
*
|
||||
* @param id 简章记录ID
|
||||
* @param response HttpServletResponse
|
||||
*/
|
||||
void downloadPdfById(Long id, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 批量下载多页PDF(每个企业一页)
|
||||
*
|
||||
* @param enterpriseIds 企业ID列表
|
||||
* @param response HttpServletResponse
|
||||
*/
|
||||
void downloadBatchPdf(List<Long> enterpriseIds, HttpServletResponse response);
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package org.dromara.zsjy.service;
|
||||
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseJobBo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业招聘岗位Service接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
public interface IZsjyEnterpriseJobService {
|
||||
|
||||
/**
|
||||
* 查询岗位
|
||||
*
|
||||
* @param id 岗位主键
|
||||
* @return 岗位
|
||||
*/
|
||||
ZsjyEnterpriseJobVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 按企业ID查询岗位列表
|
||||
*
|
||||
* @param enterpriseId 企业ID
|
||||
* @return 岗位列表
|
||||
*/
|
||||
List<ZsjyEnterpriseJobVo> queryListByEnterpriseId(Long enterpriseId);
|
||||
|
||||
/**
|
||||
* 查询岗位列表
|
||||
*/
|
||||
List<ZsjyEnterpriseJobVo> queryList(ZsjyEnterpriseJobBo bo);
|
||||
|
||||
/**
|
||||
* 新增岗位
|
||||
*/
|
||||
Boolean insertByBo(ZsjyEnterpriseJobBo bo);
|
||||
|
||||
/**
|
||||
* 批量新增岗位(用于企业新增/编辑时批量插入岗位)
|
||||
*
|
||||
* @param enterpriseId 企业ID
|
||||
* @param jobList 岗位列表
|
||||
* @return 结果
|
||||
*/
|
||||
Boolean insertBatch(Long enterpriseId, List<ZsjyEnterpriseJobBo> jobList);
|
||||
|
||||
/**
|
||||
* 修改岗位
|
||||
*/
|
||||
Boolean updateByBo(ZsjyEnterpriseJobBo bo);
|
||||
|
||||
/**
|
||||
* 删除岗位
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 按企业ID删除岗位(用于企业删除时级联删除)
|
||||
*/
|
||||
Boolean deleteByEnterpriseIds(Collection<Long> enterpriseIds);
|
||||
|
||||
}
|
||||
+29
-11
@@ -2,6 +2,7 @@ package org.dromara.zsjy.service;
|
||||
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseBo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseJobBo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
@@ -11,7 +12,7 @@ import java.util.List;
|
||||
/**
|
||||
* 企业信息Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author Lion Li (改造: Cursor AI 2026-03-30)
|
||||
* @date 2023-07-05
|
||||
*/
|
||||
public interface IZsjyEnterpriseService {
|
||||
@@ -25,23 +26,25 @@ public interface IZsjyEnterpriseService {
|
||||
ZsjyEnterpriseVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询企业信息列表
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 企业信息集合
|
||||
* 查询企业信息列表(包含岗位数量)
|
||||
*/
|
||||
TableDataInfo<ZsjyEnterpriseVo> queryPageList(ZsjyEnterpriseBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询企业信息列表
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 企业信息集合
|
||||
*/
|
||||
List<ZsjyEnterpriseVo> queryList(ZsjyEnterpriseBo bo);
|
||||
|
||||
/**
|
||||
* 新增企业信息
|
||||
* 查询企业详情(包含岗位列表)
|
||||
*
|
||||
* @param id 企业ID
|
||||
* @return 企业信息(含岗位列表)
|
||||
*/
|
||||
ZsjyEnterpriseVo queryWithJobs(Long id);
|
||||
|
||||
/**
|
||||
* 新增企业信息(同时新增岗位列表)
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 结果
|
||||
@@ -49,7 +52,7 @@ public interface IZsjyEnterpriseService {
|
||||
Boolean insertByBo(ZsjyEnterpriseBo bo);
|
||||
|
||||
/**
|
||||
* 修改企业信息
|
||||
* 修改企业信息(同时更新岗位列表)
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 结果
|
||||
@@ -57,7 +60,7 @@ public interface IZsjyEnterpriseService {
|
||||
Boolean updateByBo(ZsjyEnterpriseBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除企业信息信息
|
||||
* 校验并批量删除企业信息
|
||||
*
|
||||
* @param ids 需要删除的企业信息主键集合
|
||||
* @param isValid 是否校验,true-删除前校验,false-不校验
|
||||
@@ -65,4 +68,19 @@ public interface IZsjyEnterpriseService {
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 根据企业名称查询(用于导入时唯一性校验)
|
||||
*/
|
||||
ZsjyEnterpriseVo queryByName(String enterpriseName);
|
||||
|
||||
/**
|
||||
* 批量导入企业及岗位信息
|
||||
*
|
||||
* @param boList 企业及岗位列表
|
||||
* @param isUpdateSupport 是否支持更新(同名覆盖)
|
||||
* @param operName 操作人
|
||||
* @return 导入结果
|
||||
*/
|
||||
String importData(List<ZsjyEnterpriseBo> boList, Boolean isUpdateSupport, String operName);
|
||||
|
||||
}
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
package org.dromara.zsjy.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseBrochure;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseBrochureVo;
|
||||
import org.dromara.zsjy.mapper.ZsjyEnterpriseBrochureMapper;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseBrochureService;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.itextpdf.text.*;
|
||||
import com.itextpdf.text.pdf.*;
|
||||
import com.itextpdf.text.pdf.PdfCopy;
|
||||
|
||||
/**
|
||||
* 招聘简章PDF生成Service实现类
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ZsjyEnterpriseBrochureServiceImpl implements IZsjyEnterpriseBrochureService {
|
||||
|
||||
private final IZsjyEnterpriseService enterpriseService;
|
||||
private final ZsjyEnterpriseBrochureMapper brochureMapper;
|
||||
|
||||
@Value("${ruoyi.profile:./uploadPath}")
|
||||
private String uploadPath;
|
||||
|
||||
private static final String BROCHURE_DIR = "enterprise-brochure";
|
||||
private static final String PDF_EXTENSION = ".pdf";
|
||||
|
||||
private static final Map<Long, String> PDF_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ZsjyEnterpriseBrochureVo generateBrochure(Long enterpriseId) {
|
||||
ZsjyEnterpriseVo enterprise = enterpriseService.queryWithJobs(enterpriseId);
|
||||
if (enterprise == null) {
|
||||
throw new ServiceException("企业信息不存在");
|
||||
}
|
||||
if (CollUtil.isEmpty(enterprise.getJobList())) {
|
||||
throw new ServiceException("该企业下没有招聘岗位,无法生成简章");
|
||||
}
|
||||
|
||||
try {
|
||||
String dateDir = java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
Path storageDir = Paths.get(uploadPath, BROCHURE_DIR, dateDir);
|
||||
Files.createDirectories(storageDir);
|
||||
|
||||
String fileName = enterprise.getEnterpriseName() + "_" + System.currentTimeMillis() + PDF_EXTENSION;
|
||||
Path filePath = storageDir.resolve(fileName);
|
||||
|
||||
generatePdfDocument(enterprise, filePath);
|
||||
|
||||
ZsjyEnterpriseBrochure brochure = new ZsjyEnterpriseBrochure();
|
||||
brochure.setEnterpriseId(enterpriseId);
|
||||
brochure.setBrochurePath(filePath.toString());
|
||||
brochure.setFileName(fileName);
|
||||
brochure.setFileSize(Files.size(filePath));
|
||||
brochure.setGenerateBy(LoginHelper.getUserId());
|
||||
brochure.setGenerateByName(LoginHelper.getUsername());
|
||||
brochure.setGenerateTime(new Date());
|
||||
brochureMapper.insert(brochure);
|
||||
|
||||
PDF_CACHE.put(enterpriseId, filePath.toString());
|
||||
|
||||
log.info("成功生成企业招聘简章PDF,企业ID: {}, 文件: {}", enterpriseId, filePath);
|
||||
ZsjyEnterpriseBrochureVo vo = MapstructUtils.convert(brochure, ZsjyEnterpriseBrochureVo.class);
|
||||
vo.setEnterpriseName(enterprise.getEnterpriseName());
|
||||
return vo;
|
||||
} catch (Exception e) {
|
||||
log.error("生成招聘简章PDF失败,企业ID: {}", enterpriseId, e);
|
||||
throw new ServiceException("生成招聘简章失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public java.util.List<ZsjyEnterpriseBrochureVo> generateBrochureBatch(java.util.List<Long> enterpriseIds) {
|
||||
java.util.List<ZsjyEnterpriseBrochureVo> results = new ArrayList<>();
|
||||
for (Long id : enterpriseIds) {
|
||||
try {
|
||||
results.add(generateBrochure(id));
|
||||
} catch (Exception e) {
|
||||
log.error("批量生成简章失败,企业ID: {}", id, e);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadPdf(Long enterpriseId, HttpServletResponse response) {
|
||||
downloadPdf(enterpriseId, null, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadPdf(Long enterpriseId, String fileName, HttpServletResponse response) {
|
||||
String filePath = PDF_CACHE.get(enterpriseId);
|
||||
if (StringUtils.isBlank(filePath)) {
|
||||
log.debug("PDF缓存未命中,从数据库查询,企业ID: {}", enterpriseId);
|
||||
ZsjyEnterpriseBrochureVo latest = getLatestBrochure(enterpriseId);
|
||||
if (latest == null) {
|
||||
log.error("未找到简章记录,企业ID: {}", enterpriseId);
|
||||
throw new ServiceException("未找到该企业的简章记录,请先生成");
|
||||
}
|
||||
if (StringUtils.isBlank(latest.getBrochurePath())) {
|
||||
log.error("简章记录存在但文件路径为空,简章ID: {}", latest.getId());
|
||||
throw new ServiceException("简章文件路径不存在,请重新生成");
|
||||
}
|
||||
filePath = latest.getBrochurePath();
|
||||
PDF_CACHE.put(enterpriseId, filePath);
|
||||
// 如果没有指定文件名且查询到了企业名称,使用企业名称
|
||||
if (StringUtils.isBlank(fileName) && StringUtils.isNotBlank(latest.getEnterpriseName())) {
|
||||
fileName = latest.getEnterpriseName();
|
||||
}
|
||||
}
|
||||
log.info("准备下载PDF,文件路径: {}", filePath);
|
||||
outputFile(filePath, fileName, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZsjyEnterpriseBrochureVo getLatestBrochure(Long enterpriseId) {
|
||||
LambdaQueryWrapper<ZsjyEnterpriseBrochure> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyEnterpriseBrochure::getEnterpriseId, enterpriseId);
|
||||
lqw.orderByDesc(ZsjyEnterpriseBrochure::getCreateTime);
|
||||
lqw.last("LIMIT 1");
|
||||
ZsjyEnterpriseBrochure brochure = brochureMapper.selectOne(lqw);
|
||||
if (brochure == null) {
|
||||
return null;
|
||||
}
|
||||
ZsjyEnterpriseBrochureVo vo = MapstructUtils.convert(brochure, ZsjyEnterpriseBrochureVo.class);
|
||||
ZsjyEnterpriseVo enterprise = enterpriseService.queryById(enterpriseId);
|
||||
if (enterprise != null) {
|
||||
vo.setEnterpriseName(enterprise.getEnterpriseName());
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadPdfById(Long id, HttpServletResponse response) {
|
||||
ZsjyEnterpriseBrochure brochure = brochureMapper.selectById(id);
|
||||
if (brochure == null || StringUtils.isBlank(brochure.getBrochurePath())) {
|
||||
throw new ServiceException("PDF文件不存在");
|
||||
}
|
||||
outputFile(brochure.getBrochurePath(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadBatchPdf(java.util.List<Long> enterpriseIds, HttpServletResponse response) {
|
||||
if (CollUtil.isEmpty(enterpriseIds)) {
|
||||
throw new ServiceException("请选择要下载的企业");
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建一个内存中的 PDF
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Rectangle pageSize = PageSize.A4;
|
||||
Document document = new Document(pageSize, 0, 0, 0, 0);
|
||||
PdfCopy copy = new PdfCopy(document, baos);
|
||||
document.open();
|
||||
|
||||
BaseFont baseFont = resolveChineseBaseFont();
|
||||
|
||||
int pageNumber = 1;
|
||||
for (Long enterpriseId : enterpriseIds) {
|
||||
try {
|
||||
ZsjyEnterpriseVo enterprise = enterpriseService.queryWithJobs(enterpriseId);
|
||||
if (enterprise == null) {
|
||||
log.warn("企业信息不存在,跳过,企业ID: {}", enterpriseId);
|
||||
continue;
|
||||
}
|
||||
if (CollUtil.isEmpty(enterprise.getJobList())) {
|
||||
log.warn("该企业下没有招聘岗位,跳过,企业ID: {}", enterpriseId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 为每个企业创建一页 PDF
|
||||
PdfReader reader = generateSinglePagePdf(enterprise, baseFont);
|
||||
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
|
||||
PdfImportedPage page = copy.getImportedPage(reader, i);
|
||||
copy.addPage(page);
|
||||
}
|
||||
reader.close();
|
||||
pageNumber++;
|
||||
} catch (Exception e) {
|
||||
log.error("处理企业PDF失败,企业ID: {}", enterpriseId, e);
|
||||
}
|
||||
}
|
||||
|
||||
document.close();
|
||||
|
||||
// 检查是否有内容
|
||||
if (pageNumber == 1) {
|
||||
throw new ServiceException("没有可生成的简章内容");
|
||||
}
|
||||
|
||||
// 输出到响应
|
||||
byte[] pdfBytes = baos.toByteArray();
|
||||
response.setContentType("application/pdf");
|
||||
String fileName = "企业招聘简章_" + System.currentTimeMillis() + ".pdf";
|
||||
String encodedFileName = java.net.URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
|
||||
response.setContentLengthLong(pdfBytes.length);
|
||||
response.getOutputStream().write(pdfBytes);
|
||||
response.getOutputStream().flush();
|
||||
|
||||
log.info("批量下载PDF完成,共 {} 页", pageNumber - 1);
|
||||
} catch (ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("批量下载PDF失败", e);
|
||||
throw new ServiceException("批量下载PDF失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为单个企业生成一页 PDF(返回 PdfReader)
|
||||
*/
|
||||
private PdfReader generateSinglePagePdf(ZsjyEnterpriseVo enterprise, BaseFont baseFont) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Rectangle pageSize = PageSize.A4;
|
||||
Document document = new Document(pageSize, 0, 0, 0, 0);
|
||||
PdfWriter writer = PdfWriter.getInstance(document, baos);
|
||||
document.open();
|
||||
|
||||
float pageW = pageSize.getWidth();
|
||||
float pageH = pageSize.getHeight();
|
||||
|
||||
Image bg = loadBrochureBackgroundImage();
|
||||
boolean hasBg = bg != null;
|
||||
if (hasBg) {
|
||||
bg.scaleAbsolute(pageW, pageH);
|
||||
bg.setAbsolutePosition(0, 0);
|
||||
writer.getDirectContentUnder().addImage(bg);
|
||||
}
|
||||
|
||||
PdfContentByte canvas = writer.getDirectContent();
|
||||
|
||||
BaseColor headerFooterColor = hasBg ? BaseColor.WHITE : new BaseColor(26, 82, 118);
|
||||
Font headerNameFont = new Font(baseFont, 12, Font.BOLD, headerFooterColor);
|
||||
Font footerFont = new Font(baseFont, 9.5f, Font.BOLD, headerFooterColor);
|
||||
Font bodyHeadingFont = new Font(baseFont, 11, Font.BOLD, new BaseColor(0, 80, 140));
|
||||
Font bodyFont = new Font(baseFont, 10, Font.NORMAL, new BaseColor(30, 30, 30));
|
||||
Font pageNoFont = new Font(baseFont, 9, Font.NORMAL, new BaseColor(90, 90, 90));
|
||||
|
||||
// 左上蓝条:企业名称
|
||||
String enterpriseName = StringUtils.defaultString(enterprise.getEnterpriseName(), "—");
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(enterpriseName, headerNameFont),
|
||||
70f, baselineFromTopPx(78f), 0);
|
||||
|
||||
// 中间内容区
|
||||
ColumnText bodyColumn = new ColumnText(canvas);
|
||||
float bodyLlx = 48f;
|
||||
float bodyLly = baselineFromTopPx(706f);
|
||||
float bodyUrx = 547f;
|
||||
float bodyUry = baselineFromTopPx(120f);
|
||||
bodyColumn.setSimpleColumn(bodyLlx, bodyLly, bodyUrx, bodyUry);
|
||||
bodyColumn.setLeading(13f);
|
||||
|
||||
if (StringUtils.isNotBlank(enterprise.getEnterpriseIntro())) {
|
||||
bodyColumn.addElement(new Paragraph("企业简介", bodyHeadingFont));
|
||||
bodyColumn.addElement(new Paragraph(enterprise.getEnterpriseIntro(), bodyFont));
|
||||
bodyColumn.addElement(new Paragraph(" ", bodyFont));
|
||||
}
|
||||
for (ZsjyEnterpriseJobVo job : enterprise.getJobList()) {
|
||||
bodyColumn.addElement(new Paragraph("岗位名称:" + nvl(job.getPositionName()), bodyHeadingFont));
|
||||
bodyColumn.addElement(new Paragraph("岗位要求:" + nvl(job.getRequirements()), bodyFont));
|
||||
bodyColumn.addElement(new Paragraph("待遇:" + nvl(job.getBenefits()), bodyFont));
|
||||
bodyColumn.addElement(new Paragraph(" ", bodyFont));
|
||||
}
|
||||
|
||||
int flowStatus = bodyColumn.go();
|
||||
if (ColumnText.hasMoreText(flowStatus)) {
|
||||
log.warn("招聘简章正文超出模板区域,内容可能被截断,企业: {}", enterprise.getEnterpriseName());
|
||||
}
|
||||
|
||||
// 底部蓝条
|
||||
float footerX = 72f;
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
|
||||
new Phrase("单位地址:" + nvl(enterprise.getAddress()), footerFont),
|
||||
footerX, baselineFromTopPx(722f), 0);
|
||||
|
||||
String contactLine = "联系人:" + nvl(enterprise.getContactName());
|
||||
if (StringUtils.isNotBlank(enterprise.getContactPhone())) {
|
||||
contactLine += " 联系电话:" + enterprise.getContactPhone();
|
||||
}
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(contactLine, footerFont),
|
||||
footerX, baselineFromTopPx(740f), 0);
|
||||
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
|
||||
new Phrase("电子邮箱:" + nvl(enterprise.getEmail()), footerFont),
|
||||
footerX, baselineFromTopPx(758f), 0);
|
||||
|
||||
// 页码
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, new Phrase("—1—", pageNoFont),
|
||||
pageW / 2f, baselineFromTopPx(778f), 0);
|
||||
|
||||
document.close();
|
||||
|
||||
return new PdfReader(baos.toByteArray());
|
||||
}
|
||||
|
||||
private void outputFile(String filePath, HttpServletResponse response) {
|
||||
outputFile(filePath, null, response);
|
||||
}
|
||||
|
||||
private void outputFile(String filePath, String customFileName, HttpServletResponse response) {
|
||||
try {
|
||||
Path path = Paths.get(filePath);
|
||||
if (!Files.exists(path)) {
|
||||
log.error("PDF文件不存在: {}", filePath);
|
||||
throw new ServiceException("PDF文件不存在");
|
||||
}
|
||||
|
||||
long fileSize = Files.size(path);
|
||||
log.info("开始输出PDF文件: {}, 大小: {} bytes", filePath, fileSize);
|
||||
|
||||
response.setContentType("application/pdf");
|
||||
|
||||
String fileNameStr;
|
||||
if (StringUtils.isNotBlank(customFileName)) {
|
||||
// 使用自定义文件名
|
||||
fileNameStr = customFileName + ".pdf";
|
||||
} else {
|
||||
// 使用原文件名
|
||||
fileNameStr = path.getFileName().toString();
|
||||
}
|
||||
|
||||
String encodedFileName = java.net.URLEncoder.encode(fileNameStr, "UTF-8").replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
|
||||
response.setContentLengthLong(fileSize);
|
||||
|
||||
try (InputStream inputStream = Files.newInputStream(path);
|
||||
OutputStream outputStream = response.getOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
long totalRead = 0;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
totalRead += bytesRead;
|
||||
}
|
||||
outputStream.flush();
|
||||
log.info("PDF文件输出完成,已传输: {} bytes", totalRead);
|
||||
}
|
||||
} catch (ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("输出PDF文件失败: {}", filePath, e);
|
||||
throw new ServiceException("下载PDF文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校园招聘简章背景模板(PNG 与 A4 对齐,资源路径 classpath:/brochure/recruitment-brochure-bg.png)
|
||||
*/
|
||||
private static final String BROCHURE_BG_RESOURCE = "/brochure/recruitment-brochure-bg.png";
|
||||
|
||||
/**
|
||||
* 模板图高度(像素与 PDF 点一致 595×841),用于将「距顶部像素」换算为 PDF 纵坐标
|
||||
*/
|
||||
private static final float TEMPLATE_H = 841f;
|
||||
|
||||
private Image loadBrochureBackgroundImage() {
|
||||
try (InputStream in = getClass().getResourceAsStream(BROCHURE_BG_RESOURCE)) {
|
||||
if (in == null) {
|
||||
log.warn("未找到招聘简章背景图: {},将生成无背景 PDF", BROCHURE_BG_RESOURCE);
|
||||
return null;
|
||||
}
|
||||
byte[] bytes = in.readAllBytes();
|
||||
return Image.getInstance(bytes);
|
||||
} catch (Exception e) {
|
||||
log.warn("加载招聘简章背景图失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 模板图「距顶部」的像素行号 → PDF 基线纵坐标(原点在页面左下) */
|
||||
private static float baselineFromTopPx(float yFromTopPx) {
|
||||
return TEMPLATE_H - yFromTopPx;
|
||||
}
|
||||
|
||||
private void generatePdfDocument(ZsjyEnterpriseVo enterprise, Path filePath) throws DocumentException, IOException {
|
||||
Rectangle pageSize = PageSize.A4;
|
||||
Document document = new Document(pageSize, 0, 0, 0, 0);
|
||||
PdfWriter writer = PdfWriter.getInstance(document, Files.newOutputStream(filePath));
|
||||
document.open();
|
||||
|
||||
BaseFont baseFont = resolveChineseBaseFont();
|
||||
|
||||
float pageW = pageSize.getWidth();
|
||||
float pageH = pageSize.getHeight();
|
||||
|
||||
Image bg = loadBrochureBackgroundImage();
|
||||
boolean hasBg = bg != null;
|
||||
if (hasBg) {
|
||||
bg.scaleAbsolute(pageW, pageH);
|
||||
bg.setAbsolutePosition(0, 0);
|
||||
writer.getDirectContentUnder().addImage(bg);
|
||||
}
|
||||
|
||||
PdfContentByte canvas = writer.getDirectContent();
|
||||
|
||||
BaseColor headerFooterColor = hasBg ? BaseColor.WHITE : new BaseColor(26, 82, 118);
|
||||
Font headerNameFont = new Font(baseFont, 12, Font.BOLD, headerFooterColor);
|
||||
Font footerFont = new Font(baseFont, 9.5f, Font.BOLD, headerFooterColor);
|
||||
Font bodyHeadingFont = new Font(baseFont, 11, Font.BOLD, new BaseColor(0, 80, 140));
|
||||
Font bodyFont = new Font(baseFont, 10, Font.NORMAL, new BaseColor(30, 30, 30));
|
||||
Font pageNoFont = new Font(baseFont, 9, Font.NORMAL, new BaseColor(90, 90, 90));
|
||||
|
||||
// 左上蓝条原占位「企业名称」→ 叠加真实企业名称(白字)
|
||||
String enterpriseName = StringUtils.defaultString(enterprise.getEnterpriseName(), "—");
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(enterpriseName, headerNameFont),
|
||||
70f, baselineFromTopPx(78f), 0);
|
||||
|
||||
// 中间留白区:企业简介 + 各岗位(与模板图 595×841 标定:顶约 120px,底约 706px)
|
||||
ColumnText bodyColumn = new ColumnText(canvas);
|
||||
float bodyLlx = 48f;
|
||||
float bodyLly = baselineFromTopPx(706f);
|
||||
float bodyUrx = 547f;
|
||||
// 顶部边界向下调整,避免遮盖右侧蓝色设计区域
|
||||
float bodyUry = baselineFromTopPx(120f);
|
||||
bodyColumn.setSimpleColumn(bodyLlx, bodyLly, bodyUrx, bodyUry);
|
||||
bodyColumn.setLeading(13f);
|
||||
|
||||
if (StringUtils.isNotBlank(enterprise.getEnterpriseIntro())) {
|
||||
bodyColumn.addElement(new Paragraph("企业简介", bodyHeadingFont));
|
||||
bodyColumn.addElement(new Paragraph(enterprise.getEnterpriseIntro(), bodyFont));
|
||||
bodyColumn.addElement(new Paragraph(" ", bodyFont));
|
||||
}
|
||||
for (ZsjyEnterpriseJobVo job : enterprise.getJobList()) {
|
||||
bodyColumn.addElement(new Paragraph("岗位名称:" + nvl(job.getPositionName()), bodyHeadingFont));
|
||||
bodyColumn.addElement(new Paragraph("岗位要求:" + nvl(job.getRequirements()), bodyFont));
|
||||
bodyColumn.addElement(new Paragraph("待遇:" + nvl(job.getBenefits()), bodyFont));
|
||||
bodyColumn.addElement(new Paragraph(" ", bodyFont));
|
||||
}
|
||||
|
||||
int flowStatus = bodyColumn.go();
|
||||
if (ColumnText.hasMoreText(flowStatus)) {
|
||||
log.warn("招聘简章正文超出模板中间区域,内容可能被截断,企业: {}", enterprise.getEnterpriseName());
|
||||
}
|
||||
|
||||
// 底部蓝条标签旁叠加字段值(白字):单位地址、联系人、电子邮箱
|
||||
float footerX = 72f;
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
|
||||
new Phrase("单位地址:" + nvl(enterprise.getAddress()), footerFont),
|
||||
footerX, baselineFromTopPx(722f), 0);
|
||||
|
||||
String contactLine = "联系人:" + nvl(enterprise.getContactName());
|
||||
if (StringUtils.isNotBlank(enterprise.getContactPhone())) {
|
||||
contactLine += " 联系电话:" + enterprise.getContactPhone();
|
||||
}
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(contactLine, footerFont),
|
||||
footerX, baselineFromTopPx(740f), 0);
|
||||
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
|
||||
new Phrase("电子邮箱:" + nvl(enterprise.getEmail()), footerFont),
|
||||
footerX, baselineFromTopPx(758f), 0);
|
||||
|
||||
// 模板底部「—1—」页码区(单页简章固定为 1)
|
||||
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, new Phrase("—1—", pageNoFont),
|
||||
pageW / 2f, baselineFromTopPx(778f), 0);
|
||||
|
||||
document.close();
|
||||
}
|
||||
|
||||
private BaseFont resolveChineseBaseFont() {
|
||||
BaseFont baseFont = null;
|
||||
boolean fontLoaded = false;
|
||||
try {
|
||||
String[] fontPaths = {
|
||||
"/System/Library/Fonts/STHeiti Light.ttc",
|
||||
"/System/Library/Fonts/PingFang.ttc",
|
||||
"/System/Library/Fonts/STSongti.ttf",
|
||||
"/Library/Fonts/Arial Unicode.ttf",
|
||||
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
||||
"/usr/share/fonts/truetype/arphic/uming.ttc",
|
||||
"STSong-Light",
|
||||
"STSongStd-Light",
|
||||
"SIMHEI",
|
||||
"SIMSUN"
|
||||
};
|
||||
|
||||
for (String fontPath : fontPaths) {
|
||||
try {
|
||||
if (fontPath.startsWith("/")) {
|
||||
File fontFile = new File(fontPath);
|
||||
if (fontFile.exists()) {
|
||||
baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
|
||||
fontLoaded = true;
|
||||
log.info("成功加载中文字体: {}", fontPath);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
baseFont = BaseFont.createFont(fontPath, "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
|
||||
fontLoaded = true;
|
||||
log.info("成功加载中文字体: {}", fontPath);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("字体加载失败: {}, 尝试下一个", fontPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fontLoaded) {
|
||||
baseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
|
||||
log.warn("无法加载中文字体,使用默认字体,中文可能无法正确显示");
|
||||
}
|
||||
} catch (DocumentException | IOException e) {
|
||||
log.error("PDF字体初始化失败", e);
|
||||
throw new ServiceException("PDF字体初始化失败,请检查字体配置");
|
||||
}
|
||||
if (baseFont == null) {
|
||||
throw new ServiceException("PDF字体初始化失败");
|
||||
}
|
||||
return baseFont;
|
||||
}
|
||||
|
||||
private String nvl(String str) {
|
||||
return str == null ? "-" : str;
|
||||
}
|
||||
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package org.dromara.zsjy.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseJobBo;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseJob;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo;
|
||||
import org.dromara.zsjy.mapper.ZsjyEnterpriseJobMapper;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseJobService;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业招聘岗位Service业务层处理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ZsjyEnterpriseJobServiceImpl implements IZsjyEnterpriseJobService {
|
||||
|
||||
private final ZsjyEnterpriseJobMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public ZsjyEnterpriseJobVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZsjyEnterpriseJobVo> queryListByEnterpriseId(Long enterpriseId) {
|
||||
LambdaQueryWrapper<ZsjyEnterpriseJob> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyEnterpriseJob::getEnterpriseId, enterpriseId);
|
||||
lqw.eq(ZsjyEnterpriseJob::getStatus, "0");
|
||||
lqw.orderByAsc(ZsjyEnterpriseJob::getCreateTime);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZsjyEnterpriseJobVo> queryList(ZsjyEnterpriseJobBo bo) {
|
||||
LambdaQueryWrapper<ZsjyEnterpriseJob> lqw = buildQueryWrapper(bo);
|
||||
lqw.orderByDesc(ZsjyEnterpriseJob::getCreateTime);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<ZsjyEnterpriseJob> buildQueryWrapper(ZsjyEnterpriseJobBo bo) {
|
||||
LambdaQueryWrapper<ZsjyEnterpriseJob> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getEnterpriseId() != null, ZsjyEnterpriseJob::getEnterpriseId, bo.getEnterpriseId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getPositionName()), ZsjyEnterpriseJob::getPositionName, bo.getPositionName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), ZsjyEnterpriseJob::getStatus, bo.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(ZsjyEnterpriseJobBo bo) {
|
||||
ZsjyEnterpriseJob add = MapstructUtils.convert(bo, ZsjyEnterpriseJob.class);
|
||||
if (StringUtils.isEmpty(add.getStatus())) {
|
||||
add.setStatus("0");
|
||||
}
|
||||
return baseMapper.insert(add) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertBatch(Long enterpriseId, List<ZsjyEnterpriseJobBo> jobList) {
|
||||
if (jobList == null || jobList.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (ZsjyEnterpriseJobBo bo : jobList) {
|
||||
ZsjyEnterpriseJob add = MapstructUtils.convert(bo, ZsjyEnterpriseJob.class);
|
||||
add.setId(null); // 清除旧ID,让MyBatis-Plus自动生成新的雪花ID
|
||||
add.setEnterpriseId(enterpriseId);
|
||||
if (StringUtils.isEmpty(add.getStatus())) {
|
||||
add.setStatus("0");
|
||||
}
|
||||
baseMapper.insert(add);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(ZsjyEnterpriseJobBo bo) {
|
||||
ZsjyEnterpriseJob update = MapstructUtils.convert(bo, ZsjyEnterpriseJob.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteByEnterpriseIds(Collection<Long> enterpriseIds) {
|
||||
if (enterpriseIds == null || enterpriseIds.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (Long enterpriseId : enterpriseIds) {
|
||||
LambdaQueryWrapper<ZsjyEnterpriseJob> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(ZsjyEnterpriseJob::getEnterpriseId, enterpriseId);
|
||||
baseMapper.delete(wrapper);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+147
-64
@@ -1,144 +1,227 @@
|
||||
package org.dromara.zsjy.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseBo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnterpriseJobBo;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterprise;
|
||||
import org.dromara.zsjy.domain.ZsjyJobPosition;
|
||||
import org.dromara.zsjy.domain.ZsjyEnterpriseJob;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo;
|
||||
import org.dromara.zsjy.mapper.ZsjyEnterpriseMapper;
|
||||
import org.dromara.zsjy.mapper.ZsjyJobPositionMapper;
|
||||
import org.dromara.zsjy.mapper.ZsjyEnterpriseJobMapper;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseService;
|
||||
import org.dromara.zsjy.service.IZsjyEnterpriseJobService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业信息Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author Lion Li (改造: Cursor AI 2026-03-30)
|
||||
* @date 2023-07-05
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ZsjyEnterpriseServiceImpl implements IZsjyEnterpriseService {
|
||||
|
||||
private final ZsjyEnterpriseMapper baseMapper;
|
||||
private final ZsjyJobPositionMapper jobPositionMapper;
|
||||
private final ZsjyEnterpriseJobMapper jobMapper;
|
||||
private final IZsjyEnterpriseJobService jobService;
|
||||
|
||||
/**
|
||||
* 查询企业信息
|
||||
*
|
||||
* @param id 企业信息主键
|
||||
* @return 企业信息
|
||||
*/
|
||||
@Override
|
||||
public ZsjyEnterpriseVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询企业信息列表
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 企业信息
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<ZsjyEnterpriseVo> queryPageList(ZsjyEnterpriseBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<ZsjyEnterprise> lqw = buildQueryWrapper(bo);
|
||||
Page<ZsjyEnterpriseVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
Page<ZsjyEnterpriseVo> page = pageQuery.build();
|
||||
Page<ZsjyEnterpriseVo> result = baseMapper.selectVoPageWithJobCount(page, bo);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询企业信息列表
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 企业信息
|
||||
*/
|
||||
@Override
|
||||
public List<ZsjyEnterpriseVo> queryList(ZsjyEnterpriseBo bo) {
|
||||
LambdaQueryWrapper<ZsjyEnterprise> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZsjyEnterpriseVo queryWithJobs(Long id) {
|
||||
ZsjyEnterpriseVo vo = baseMapper.selectVoById(id);
|
||||
if (vo != null) {
|
||||
LambdaQueryWrapper<ZsjyEnterpriseJob> jobWrapper = Wrappers.lambdaQuery();
|
||||
jobWrapper.eq(ZsjyEnterpriseJob::getEnterpriseId, id);
|
||||
jobWrapper.eq(ZsjyEnterpriseJob::getStatus, "0");
|
||||
jobWrapper.orderByAsc(ZsjyEnterpriseJob::getCreateTime);
|
||||
List<ZsjyEnterpriseJobVo> jobs = jobMapper.selectVoList(jobWrapper);
|
||||
vo.setJobList(jobs);
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<ZsjyEnterprise> buildQueryWrapper(ZsjyEnterpriseBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<ZsjyEnterprise> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getEnterpriseName()), ZsjyEnterprise::getEnterpriseName, bo.getEnterpriseName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getIndustry()), ZsjyEnterprise::getIndustry, bo.getIndustry());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getMainBusiness()), ZsjyEnterprise::getMainBusiness, bo.getMainBusiness());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEnterpriseScale()), ZsjyEnterprise::getEnterpriseScale, bo.getEnterpriseScale());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), ZsjyEnterprise::getStatus, bo.getStatus());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getContactName()), ZsjyEnterprise::getContactName, bo.getContactName());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getContactPhone()), ZsjyEnterprise::getContactPhone, bo.getContactPhone());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getEmail()), ZsjyEnterprise::getEmail, bo.getEmail());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAddress()), ZsjyEnterprise::getAddress, bo.getAddress());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), ZsjyEnterprise::getStatus, bo.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增企业信息
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(ZsjyEnterpriseBo bo) {
|
||||
ZsjyEnterprise add = MapstructUtils.convert(bo, ZsjyEnterprise.class);
|
||||
// 默认状态为正常
|
||||
if (StringUtils.isEmpty(add.getStatus())) {
|
||||
add.setStatus("0");
|
||||
}
|
||||
log.info("开始插入企业信息: {}", bo.getEnterpriseName());
|
||||
|
||||
// 手动创建实体对象并复制字段,避免MapStruct转换问题
|
||||
ZsjyEnterprise add = new ZsjyEnterprise();
|
||||
add.setEnterpriseName(bo.getEnterpriseName());
|
||||
// 处理数据库NOT NULL字段,确保不为null
|
||||
add.setIndustry(safeString(bo.getIndustry()));
|
||||
add.setMainBusiness(safeString(bo.getMainBusiness()));
|
||||
add.setEnterpriseScale(safeString(bo.getEnterpriseScale()));
|
||||
add.setEnterpriseIntro(bo.getEnterpriseIntro());
|
||||
add.setContactName(safeString(bo.getContactName()));
|
||||
add.setContactPhone(safeString(bo.getContactPhone()));
|
||||
add.setOfficePhone(bo.getOfficePhone());
|
||||
add.setEmail(safeString(bo.getEmail()));
|
||||
add.setWebsite(bo.getWebsite());
|
||||
add.setAddress(safeString(bo.getAddress()));
|
||||
add.setRemark(bo.getRemark());
|
||||
add.setStatus(StringUtils.isEmpty(bo.getStatus()) ? "0" : bo.getStatus());
|
||||
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
log.info("企业信息插入结果: {}, 企业ID: {}", flag, add.getId());
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
if (CollUtil.isNotEmpty(bo.getJobList())) {
|
||||
log.info("开始插入岗位信息,企业ID: {}, 岗位数量: {}", add.getId(), bo.getJobList().size());
|
||||
jobService.insertBatch(add.getId(), bo.getJobList());
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(ZsjyEnterpriseBo bo) {
|
||||
// 手动创建实体对象并复制字段
|
||||
ZsjyEnterprise update = new ZsjyEnterprise();
|
||||
update.setId(bo.getId());
|
||||
update.setEnterpriseName(bo.getEnterpriseName());
|
||||
update.setIndustry(safeString(bo.getIndustry()));
|
||||
update.setMainBusiness(safeString(bo.getMainBusiness()));
|
||||
update.setEnterpriseScale(safeString(bo.getEnterpriseScale()));
|
||||
update.setEnterpriseIntro(bo.getEnterpriseIntro());
|
||||
update.setContactName(safeString(bo.getContactName()));
|
||||
update.setContactPhone(safeString(bo.getContactPhone()));
|
||||
update.setOfficePhone(bo.getOfficePhone());
|
||||
update.setEmail(safeString(bo.getEmail()));
|
||||
update.setWebsite(bo.getWebsite());
|
||||
update.setAddress(safeString(bo.getAddress()));
|
||||
update.setRemark(bo.getRemark());
|
||||
update.setStatus(bo.getStatus());
|
||||
|
||||
boolean flag = baseMapper.updateById(update) > 0;
|
||||
if (flag) {
|
||||
// 先删除该企业下所有旧岗位,再统一插入新岗位
|
||||
jobService.deleteByEnterpriseIds(List.of(bo.getId()));
|
||||
if (CollUtil.isNotEmpty(bo.getJobList())) {
|
||||
jobService.insertBatch(bo.getId(), bo.getJobList());
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改企业信息
|
||||
*
|
||||
* @param bo 企业信息
|
||||
* @return 结果
|
||||
* 安全字符串转换,确保不为null
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(ZsjyEnterpriseBo bo) {
|
||||
ZsjyEnterprise update = MapstructUtils.convert(bo, ZsjyEnterprise.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
private String safeString(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除企业信息
|
||||
*
|
||||
* @param ids 需要删除的企业信息主键
|
||||
* @param isValid 是否校验,true-删除前校验,false-不校验
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// 校验是否存在关联的岗位
|
||||
for (Long id : ids) {
|
||||
LambdaQueryWrapper<ZsjyJobPosition> wrapper = Wrappers.lambdaQuery(ZsjyJobPosition.class)
|
||||
.eq(ZsjyJobPosition::getEnterpriseId, id);
|
||||
long count = jobPositionMapper.selectCount(wrapper);
|
||||
if (count > 0) {
|
||||
List<ZsjyEnterpriseJobVo> jobs = jobService.queryListByEnterpriseId(id);
|
||||
if (CollUtil.isNotEmpty(jobs)) {
|
||||
ZsjyEnterpriseVo enterprise = queryById(id);
|
||||
throw new ServiceException(enterprise.getEnterpriseName() + "存在关联岗位,不能删除");
|
||||
throw new ServiceException(enterprise.getEnterpriseName() + " 存在关联岗位,不能删除");
|
||||
}
|
||||
}
|
||||
}
|
||||
jobService.deleteByEnterpriseIds(ids);
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZsjyEnterpriseVo queryByName(String enterpriseName) {
|
||||
LambdaQueryWrapper<ZsjyEnterprise> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyEnterprise::getEnterpriseName, enterpriseName);
|
||||
return baseMapper.selectVoOne(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String importData(List<ZsjyEnterpriseBo> boList, Boolean isUpdateSupport, String operName) {
|
||||
if (boList == null || boList.isEmpty()) {
|
||||
throw new ServiceException("导入企业数据不能为空");
|
||||
}
|
||||
int successNum = 0;
|
||||
int failureNum = 0;
|
||||
StringBuilder successMsg = new StringBuilder();
|
||||
StringBuilder failureMsg = new StringBuilder();
|
||||
for (ZsjyEnterpriseBo bo : boList) {
|
||||
try {
|
||||
ZsjyEnterpriseVo exist = queryByName(bo.getEnterpriseName());
|
||||
if (exist == null) {
|
||||
insertByBo(bo);
|
||||
successNum++;
|
||||
successMsg.append("<br/>").append(successNum).append("、企业 ").append(bo.getEnterpriseName()).append(" 导入成功");
|
||||
} else if (isUpdateSupport) {
|
||||
bo.setId(exist.getId());
|
||||
updateByBo(bo);
|
||||
successNum++;
|
||||
successMsg.append("<br/>").append(successNum).append("、企业 ").append(bo.getEnterpriseName()).append(" 更新成功");
|
||||
} else {
|
||||
failureNum++;
|
||||
failureMsg.append("<br/>").append(failureNum).append("、企业 ").append(bo.getEnterpriseName()).append(" 已存在");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
failureNum++;
|
||||
String msg = "<br/>" + failureNum + "、企业 " + bo.getEnterpriseName() + " 导入失败:" + e.getMessage();
|
||||
failureMsg.append(msg);
|
||||
log.error(msg, e);
|
||||
}
|
||||
}
|
||||
if (failureNum > 0) {
|
||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
||||
throw new ServiceException(failureMsg.toString());
|
||||
} else {
|
||||
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
|
||||
}
|
||||
return successMsg.toString();
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
+31
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.zsjy.mapper.ZsjyEnterpriseBrochureMapper">
|
||||
|
||||
<resultMap type="org.dromara.zsjy.domain.vo.ZsjyEnterpriseBrochureVo" id="ZsjyEnterpriseBrochureResult">
|
||||
<id property="id" column="id"/>
|
||||
<result property="enterpriseId" column="enterprise_id"/>
|
||||
<result property="enterpriseName" column="enterprise_name"/>
|
||||
<result property="brochurePath" column="brochure_path"/>
|
||||
<result property="fileName" column="file_name"/>
|
||||
<result property="fileSize" column="file_size"/>
|
||||
<result property="generateBy" column="generate_by"/>
|
||||
<result property="generateByName" column="generate_by_name"/>
|
||||
<result property="generateTime" column="generate_time"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectVoByEnterpriseId" resultMap="ZsjyEnterpriseBrochureResult">
|
||||
SELECT b.*, e.enterprise_name
|
||||
FROM zsjy_enterprise_brochure b
|
||||
LEFT JOIN zsjy_enterprise e ON b.enterprise_id = e.id
|
||||
WHERE b.enterprise_id = #{enterpriseId}
|
||||
AND b.deleted = 0
|
||||
ORDER BY b.create_time DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.dromara.zsjy.mapper.ZsjyEnterpriseJobMapper">
|
||||
|
||||
<resultMap type="org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo" id="ZsjyEnterpriseJobResult">
|
||||
<id property="id" column="id"/>
|
||||
<result property="enterpriseId" column="enterprise_id"/>
|
||||
<result property="positionName" column="position_name"/>
|
||||
<result property="requirements" column="requirements"/>
|
||||
<result property="benefits" column="benefits"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 按企业ID查询岗位列表(用于简章展示,仅查询正常状态的岗位) -->
|
||||
<select id="selectVoListByEnterpriseId" resultMap="ZsjyEnterpriseJobResult">
|
||||
select id, enterprise_id, position_name, requirements, benefits, status,
|
||||
create_by, create_time, update_by, update_time
|
||||
from zsjy_enterprise_job
|
||||
where enterprise_id = #{enterpriseId}
|
||||
and deleted = 0
|
||||
and status = '0'
|
||||
order by create_time asc
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+127
-1
@@ -12,9 +12,12 @@
|
||||
<result property="mainBusiness" column="main_business"/>
|
||||
<result property="enterpriseScale" column="enterprise_scale"/>
|
||||
<result property="enterpriseScaleName" column="enterprise_scale_name"/>
|
||||
<result property="enterpriseIntro" column="enterprise_intro"/>
|
||||
<result property="contactName" column="contact_name"/>
|
||||
<result property="contactPhone" column="contact_phone"/>
|
||||
<result property="officePhone" column="office_phone"/>
|
||||
<result property="email" column="email"/>
|
||||
<result property="website" column="website"/>
|
||||
<result property="address" column="address"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="remark" column="remark"/>
|
||||
@@ -22,6 +25,47 @@
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="jobCount" column="job_count"/>
|
||||
<collection property="jobList" column="id"
|
||||
select="org.dromara.zsjy.mapper.ZsjyEnterpriseJobMapper.selectVoListByEnterpriseId"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 使用JOIN子查询的方式,避免嵌套select的列传递问题 -->
|
||||
<resultMap type="org.dromara.zsjy.domain.vo.ZsjyEnterpriseVo" id="ZsjyEnterpriseWithJobsResult">
|
||||
<id property="id" column="id"/>
|
||||
<result property="enterpriseName" column="enterprise_name"/>
|
||||
<result property="industry" column="industry"/>
|
||||
<result property="industryName" column="industry_name"/>
|
||||
<result property="mainBusiness" column="main_business"/>
|
||||
<result property="enterpriseScale" column="enterprise_scale"/>
|
||||
<result property="enterpriseScaleName" column="enterprise_scale_name"/>
|
||||
<result property="enterpriseIntro" column="enterprise_intro"/>
|
||||
<result property="contactName" column="contact_name"/>
|
||||
<result property="contactPhone" column="contact_phone"/>
|
||||
<result property="officePhone" column="office_phone"/>
|
||||
<result property="email" column="email"/>
|
||||
<result property="website" column="website"/>
|
||||
<result property="address" column="address"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="jobCount" column="job_count"/>
|
||||
<!-- 使用嵌套结果映射而非嵌套select,避免列值传递问题 -->
|
||||
<collection property="jobList" ofType="org.dromara.zsjy.domain.vo.ZsjyEnterpriseJobVo">
|
||||
<id property="id" column="job_id"/>
|
||||
<result property="enterpriseId" column="job_enterprise_id"/>
|
||||
<result property="positionName" column="job_position_name"/>
|
||||
<result property="requirements" column="job_requirements"/>
|
||||
<result property="benefits" column="job_benefits"/>
|
||||
<result property="status" column="job_status"/>
|
||||
<result property="createBy" column="job_create_by"/>
|
||||
<result property="createTime" column="job_create_time"/>
|
||||
<result property="updateBy" column="job_update_by"/>
|
||||
<result property="updateTime" column="job_update_time"/>
|
||||
</collection>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectZsjyEnterpriseVo">
|
||||
@@ -32,19 +76,101 @@
|
||||
e.main_business,
|
||||
e.enterprise_scale,
|
||||
d2.dict_label as enterprise_scale_name,
|
||||
e.enterprise_intro,
|
||||
e.contact_name,
|
||||
e.contact_phone,
|
||||
e.office_phone,
|
||||
e.email,
|
||||
e.website,
|
||||
e.address,
|
||||
e.status,
|
||||
e.remark,
|
||||
e.create_by,
|
||||
e.create_time,
|
||||
e.update_by,
|
||||
e.update_time
|
||||
e.update_time,
|
||||
(select count(*) from zsjy_enterprise_job j
|
||||
where j.enterprise_id = e.id and j.deleted = 0 and j.status = '0') as job_count
|
||||
from zsjy_enterprise e
|
||||
left join sys_dict_data d1 on e.industry = d1.dict_value and d1.dict_type = 'zsjy_enterprise_industry'
|
||||
left join sys_dict_data d2 on e.enterprise_scale = d2.dict_value and d2.dict_type = 'zsjy_enterprise_scale'
|
||||
</sql>
|
||||
|
||||
<sql id="selectZsjyEnterpriseVoWithJobs">
|
||||
select e.id,
|
||||
e.enterprise_name,
|
||||
e.industry,
|
||||
d1.dict_label as industry_name,
|
||||
e.main_business,
|
||||
e.enterprise_scale,
|
||||
d2.dict_label as enterprise_scale_name,
|
||||
e.enterprise_intro,
|
||||
e.contact_name,
|
||||
e.contact_phone,
|
||||
e.office_phone,
|
||||
e.email,
|
||||
e.website,
|
||||
e.address,
|
||||
e.status,
|
||||
e.remark,
|
||||
e.create_by,
|
||||
e.create_time,
|
||||
e.update_by,
|
||||
e.update_time,
|
||||
(select count(*) from zsjy_enterprise_job j
|
||||
where j.enterprise_id = e.id and j.deleted = 0 and j.status = '0') as job_count,
|
||||
j.id as job_id,
|
||||
j.enterprise_id as job_enterprise_id,
|
||||
j.position_name as job_position_name,
|
||||
j.requirements as job_requirements,
|
||||
j.benefits as job_benefits,
|
||||
j.status as job_status,
|
||||
j.create_by as job_create_by,
|
||||
j.create_time as job_create_time,
|
||||
j.update_by as job_update_by,
|
||||
j.update_time as job_update_time
|
||||
from zsjy_enterprise e
|
||||
left join zsjy_enterprise_job j on e.id = j.enterprise_id and j.deleted = 0 and j.status = '0'
|
||||
left join sys_dict_data d1 on e.industry = d1.dict_value and d1.dict_type = 'zsjy_enterprise_industry'
|
||||
left join sys_dict_data d2 on e.enterprise_scale = d2.dict_value and d2.dict_type = 'zsjy_enterprise_scale'
|
||||
</sql>
|
||||
|
||||
<!-- 查询企业详情(JOIN方式含岗位列表) -->
|
||||
<select id="selectVoWithJobsById" resultMap="ZsjyEnterpriseWithJobsResult">
|
||||
<include refid="selectZsjyEnterpriseVoWithJobs"/>
|
||||
where e.id = #{id}
|
||||
</select>
|
||||
|
||||
<!-- 分页查询企业列表(包含岗位数量,通过子查询计算) -->
|
||||
<select id="selectVoPageWithJobCount" resultMap="ZsjyEnterpriseResult">
|
||||
<include refid="selectZsjyEnterpriseVo"/>
|
||||
<where>
|
||||
<if test="bo.enterpriseName != null and bo.enterpriseName != ''">
|
||||
and e.enterprise_name like concat('%', #{bo.enterpriseName}, '%')
|
||||
</if>
|
||||
<if test="bo.industry != null and bo.industry != ''">
|
||||
and e.industry = #{bo.industry}
|
||||
</if>
|
||||
<if test="bo.enterpriseScale != null and bo.enterpriseScale != ''">
|
||||
and e.enterprise_scale = #{bo.enterpriseScale}
|
||||
</if>
|
||||
<if test="bo.status != null and bo.status != ''">
|
||||
and e.status = #{bo.status}
|
||||
</if>
|
||||
<if test="bo.contactName != null and bo.contactName != ''">
|
||||
and e.contact_name like concat('%', #{bo.contactName}, '%')
|
||||
</if>
|
||||
<if test="bo.contactPhone != null and bo.contactPhone != ''">
|
||||
and e.contact_phone like concat('%', #{bo.contactPhone}, '%')
|
||||
</if>
|
||||
<if test="bo.email != null and bo.email != ''">
|
||||
and e.email like concat('%', #{bo.email}, '%')
|
||||
</if>
|
||||
<if test="bo.address != null and bo.address != ''">
|
||||
and e.address like concat('%', #{bo.address}, '%')
|
||||
</if>
|
||||
</where>
|
||||
order by e.create_time desc
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -73,6 +73,8 @@
|
||||
|
||||
## 企业信息管理API
|
||||
|
||||
[企业信息管理API](./企业信息管理API.md) - 包含企业基本信息管理、招聘岗位管理(含子表CRUD)、招聘简章PDF生成(单个/批量)、Excel导入功能(支持企业+岗位一体化导入)。
|
||||
**重大更新(2026-03-30)**:企业信息新增企业简介、办公电话、官网字段;整合岗位管理;新增简章PDF生成和导入功能。
|
||||
|
||||
[统计报表API](./统计报表API.md) - 包含各类统计数据查询接口。
|
||||
|
||||
|
||||
+502
-397
@@ -1,454 +1,559 @@
|
||||
# 企业信息管理API接口文档
|
||||
|
||||
> 文档创建人:admin
|
||||
> 创建日期:2023-07-05
|
||||
> 文档状态:创建中
|
||||
> 文档创建人:admin
|
||||
> 创建日期:2023-07-05
|
||||
> 更新日期:2026-03-30
|
||||
> 文档状态:已完成
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文档描述了企业信息管理模块的API接口设计。企业信息管理模块包含企业基本信息管理、招聘岗位管理以及招聘海报生成功能。
|
||||
本文档描述了企业信息管理模块的API接口设计。企业信息管理模块包含企业基本信息管理、招聘岗位管理以及招聘简章PDF生成功能。
|
||||
|
||||
**核心变更(2026-03-30)**:
|
||||
- 企业信息新增「企业简介」「办公电话」「官网」字段
|
||||
- 企业信息与岗位信息在新增/编辑时一体化处理
|
||||
- 新增招聘简章PDF生成功能(单个/批量)
|
||||
- 新增Excel导入功能(支持企业+岗位一次性导入)
|
||||
- 废弃原招聘海报管理、招聘岗位管理菜单(功能整合至本模块)
|
||||
|
||||
### 1.1 基础信息
|
||||
|
||||
- 接口基础路径:`/zsjy/enterprise`
|
||||
- 请求方式:`GET`、`POST`、`PUT`、`DELETE`
|
||||
- 认证方式:Bearer Token
|
||||
- 响应格式:统一响应格式
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 接口基础路径 | `/zsjy/enterprise` |
|
||||
| 请求方式 | `GET`、`POST`、`PUT`、`DELETE` |
|
||||
| 认证方式 | Bearer Token |
|
||||
| 响应格式 | 统一响应格式 `R<T>`,列表分页用 `TableDataInfo<T>` |
|
||||
|
||||
### 1.2 数据结构
|
||||
|
||||
#### 1.2.1 企业信息 (EnterpriseVO)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | Long | 企业ID |
|
||||
| enterpriseName | String | 企业名称 |
|
||||
| industry | String | 所属行业(字典值) |
|
||||
| industryName | String | 所属行业名称(字典标签) |
|
||||
| mainBusiness | String | 主营业务(兼容保留) |
|
||||
| enterpriseScale | String | 企业规模(字典值) |
|
||||
| enterpriseScaleName | String | 企业规模名称(字典标签) |
|
||||
| enterpriseIntro | String | **新增** 企业简介(≤1500字符,用于招聘简章) |
|
||||
| contactName | String | 联系人姓名 |
|
||||
| contactPhone | String | 联系电话 |
|
||||
| officePhone | String | **新增** 办公电话 |
|
||||
| email | String | 电子邮箱 |
|
||||
| website | String | **新增** 企业官网 |
|
||||
| address | String | 企业地址 |
|
||||
| status | String | 状态(0正常 1停用) |
|
||||
| remark | String | 备注(兼容保留) |
|
||||
| jobList | List\<EnterpriseJobVO\> | **新增** 岗位列表(用于招聘简章) |
|
||||
| jobCount | Integer | **新增** 岗位数量(列表展示用) |
|
||||
| createBy | Long | 创建者 |
|
||||
| createTime | Date | 创建时间 |
|
||||
| updateBy | Long | 更新者 |
|
||||
| updateTime | Date | 更新时间 |
|
||||
|
||||
#### 1.2.2 招聘岗位信息 (EnterpriseJobVO)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | Long | 岗位ID |
|
||||
| enterpriseId | Long | 所属企业ID |
|
||||
| positionName | String | 岗位名称 |
|
||||
| requirements | String | **岗位要求**(用于招聘简章) |
|
||||
| benefits | String | **待遇**(用于招聘简章) |
|
||||
| status | String | 状态(0正常 1停用) |
|
||||
| createBy | Long | 创建者 |
|
||||
| createTime | Date | 创建时间 |
|
||||
| updateBy | Long | 更新者 |
|
||||
| updateTime | Date | 更新时间 |
|
||||
|
||||
#### 1.2.3 简章生成记录 (EnterpriseBrochureVO)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | Long | 记录ID |
|
||||
| enterpriseId | Long | 企业ID |
|
||||
| enterpriseName | String | 企业名称(关联) |
|
||||
| brochurePath | String | PDF文件存储路径 |
|
||||
| fileName | String | 文件名称 |
|
||||
| fileSize | Long | 文件大小(字节) |
|
||||
| generateBy | Long | 生成人ID |
|
||||
| generateByName | String | 生成人姓名 |
|
||||
| generateTime | Date | 生成时间 |
|
||||
| createTime | Date | 创建时间 |
|
||||
|
||||
#### 1.2.4 企业导入模板 (ZsjyEnterpriseImportVo)
|
||||
|
||||
导入模板共19列(H~AC为岗位信息,最多支持6个岗位):
|
||||
|
||||
| 列 | 字段名 | 必填 | 说明 |
|
||||
|----|--------|------|------|
|
||||
| A | 企业名称 | 是 | 唯一性校验 |
|
||||
| B | 企业简介 | 否 | ≤1500字符 |
|
||||
| C | 企业地址 | 是 | — |
|
||||
| D | 联系人 | 是 | — |
|
||||
| E | 联系电话 | 是 | 手机号格式 |
|
||||
| F | 办公电话 | 否 | — |
|
||||
| G | 电子邮箱 | 是 | 邮箱格式 |
|
||||
| H | 招聘岗位1 | 否 | 岗位名称 |
|
||||
| I | 要求1 | 否 | 岗位要求 |
|
||||
| J | 待遇1 | 否 | 待遇信息 |
|
||||
| K | 招聘岗位2 | 否 | — |
|
||||
| L | 要求2 | 否 | — |
|
||||
| M | 待遇2 | 否 | — |
|
||||
| N | 招聘岗位3 | 否 | — |
|
||||
| O | 要求3 | 否 | — |
|
||||
| P | 待遇3 | 否 | — |
|
||||
| Q | 招聘岗位4 | 否 | — |
|
||||
| R | 要求4 | 否 | — |
|
||||
| S | 待遇4 | 否 | — |
|
||||
| T | 招聘岗位5 | 否 | — |
|
||||
| U | 要求5 | 否 | — |
|
||||
| V | 待遇5 | 否 | — |
|
||||
| W | 招聘岗位6 | 否 | — |
|
||||
| X | 要求6 | 否 | — |
|
||||
| Y | 待遇6 | 否 | — |
|
||||
|
||||
---
|
||||
|
||||
## 2. 接口详细说明
|
||||
|
||||
### 2.1 查询企业信息列表
|
||||
|
||||
**接口地址**:`GET /zsjy/enterprise/list`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:list`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| enterpriseName | String | 否 | 企业名称(模糊查询) |
|
||||
| industry | String | 否 | 所属行业(字典值) |
|
||||
| enterpriseScale | String | 否 | 企业规模(字典值) |
|
||||
| status | String | 否 | 状态(0正常 1停用) |
|
||||
| contactName | String | 否 | 联系人(模糊查询) |
|
||||
| contactPhone | String | 否 | 联系电话(模糊查询) |
|
||||
| email | String | 否 | 邮箱(模糊查询) |
|
||||
| address | String | 否 | 地址(模糊查询) |
|
||||
| pageNum | Integer | 否 | 页码(默认1) |
|
||||
| pageSize | Integer | 否 | 每页条数(默认10) |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "企业ID,long型",
|
||||
"enterpriseName": "企业名称,字符串",
|
||||
"industry": "所属行业,字典值",
|
||||
"mainBusiness": "主营业务,字符串",
|
||||
"enterpriseScale": "企业规模,字典值",
|
||||
"contactName": "企业联系人姓名,字符串",
|
||||
"contactPhone": "联系电话,字符串",
|
||||
"email": "企业邮箱,字符串",
|
||||
"address": "企业地址,字符串",
|
||||
"status": "状态(0正常 1停用),字符",
|
||||
"remark": "备注,字符串",
|
||||
"createBy": "创建者,long型",
|
||||
"createTime": "创建时间,日期时间",
|
||||
"updateBy": "更新者,long型",
|
||||
"updateTime": "更新时间,日期时间"
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"total": 10,
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
"enterpriseName": "杭州某某科技有限公司",
|
||||
"industry": "01",
|
||||
"industryName": "互联网",
|
||||
"enterpriseScale": "01",
|
||||
"enterpriseScaleName": "小型企业",
|
||||
"enterpriseIntro": "公司成立于2010年,专注于软件开发...",
|
||||
"contactName": "张三",
|
||||
"contactPhone": "13800138000",
|
||||
"officePhone": "0571-88888888",
|
||||
"email": "hr@example.com",
|
||||
"website": "https://www.example.com",
|
||||
"address": "浙江省杭州市西湖区xxx路xxx号",
|
||||
"status": "0",
|
||||
"jobCount": 3,
|
||||
"createTime": "2026-03-30 10:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2.2 招聘岗位信息 (JobPositionVO)
|
||||
---
|
||||
|
||||
### 2.2 获取企业详细信息
|
||||
|
||||
**接口地址**:`GET /zsjy/enterprise/{id}`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:query`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 企业ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "岗位ID,long型",
|
||||
"enterpriseId": "所属企业ID,long型",
|
||||
"enterpriseName": "所属企业名称,字符串",
|
||||
"positionName": "岗位名称,字符串",
|
||||
"workAddress": "工作地址,字符串",
|
||||
"workTime": "工作时间,字符串",
|
||||
"salary": "薪资,字符串",
|
||||
"requiredSkills": "所需技能,字符串",
|
||||
"startDate": "有效期开始日期,日期时间",
|
||||
"endDate": "有效期结束日期,日期时间",
|
||||
"isPermanent": "是否永久有效(0否 1是),整型",
|
||||
"status": "状态(0正常 1停用),字符",
|
||||
"remark": "备注,字符串",
|
||||
"createBy": "创建者,long型",
|
||||
"createTime": "创建时间,日期时间",
|
||||
"updateBy": "更新者,long型",
|
||||
"updateTime": "更新时间,日期时间"
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2.3 招聘海报 (RecruitmentPosterVO)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "海报ID,long型",
|
||||
"enterpriseId": "企业ID,long型",
|
||||
"enterpriseName": "企业名称,字符串",
|
||||
"positionId": "岗位ID,long型",
|
||||
"positionName": "岗位名称,字符串",
|
||||
"posterPath": "海报文件路径,字符串",
|
||||
"posterType": "海报类型(1企业海报 2岗位海报),字符",
|
||||
"createBy": "创建者,long型",
|
||||
"createTime": "创建时间,日期时间",
|
||||
"updateBy": "更新者,long型",
|
||||
"updateTime": "更新时间,日期时间"
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 接口定义
|
||||
|
||||
### 2.1 企业信息管理接口
|
||||
|
||||
#### 2.1.1 获取企业列表
|
||||
|
||||
- **接口URL**:`/zsjy/enterprise/list`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:enterprise:list`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------------|----------|----------|----------|----------|-------------------------|
|
||||
| pageNum | 页码 | query | 是 | int | 默认为1 |
|
||||
| pageSize | 每页数量 | query | 是 | int | 默认为10 |
|
||||
| enterpriseName | 企业名称 | query | 否 | string | 模糊匹配 |
|
||||
| industry | 所属行业 | query | 否 | string | 精确匹配,来自数据字典 |
|
||||
| status | 状态 | query | 否 | string | 0正常 1停用 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": "总记录数",
|
||||
"rows": "企业列表数据,数组类型,每个元素为EnterpriseVO对象",
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.2 获取企业详情
|
||||
|
||||
- **接口URL**:`/zsjy/enterprise/{id}`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:enterprise:query`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------|----------|----------|----------|----------|------------|
|
||||
| id | 企业ID | path | 是 | long | 路径变量 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"data": "企业详情数据,EnterpriseVO对象",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.3 新增企业信息
|
||||
|
||||
- **接口URL**:`/zsjy/enterprise`
|
||||
- **请求方式**:`POST`
|
||||
- **接口权限**:`zsjy:enterprise:add`
|
||||
- **请求参数**:EnterpriseVO对象(不含id、createBy、createTime、updateBy、updateTime字段)
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.4 修改企业信息
|
||||
|
||||
- **接口URL**:`/zsjy/enterprise`
|
||||
- **请求方式**:`PUT`
|
||||
- **接口权限**:`zsjy:enterprise:edit`
|
||||
- **请求参数**:EnterpriseVO对象(必须包含id字段)
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.1.5 删除企业信息
|
||||
|
||||
- **接口URL**:`/zsjy/enterprise/{ids}`
|
||||
- **请求方式**:`DELETE`
|
||||
- **接口权限**:`zsjy:enterprise:remove`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------|-----------------------------|----------|----------|----------|-----------------------|
|
||||
| ids | 企业ID,多个以逗号分隔 | path | 是 | string | 例如:1 或 1,2,3 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 招聘岗位管理接口
|
||||
|
||||
#### 2.2.1 获取岗位列表
|
||||
|
||||
- **接口URL**:`/zsjy/position/list`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:position:list`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|--------------|--------------|----------|----------|----------|-------------------------|
|
||||
| pageNum | 页码 | query | 是 | int | 默认为1 |
|
||||
| pageSize | 每页数量 | query | 是 | int | 默认为10 |
|
||||
| enterpriseId | 企业ID | query | 否 | long | 精确匹配 |
|
||||
| positionName | 岗位名称 | query | 否 | string | 模糊匹配 |
|
||||
| status | 状态 | query | 否 | string | 0正常 1停用 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": "总记录数",
|
||||
"rows": "岗位列表数据,数组类型,每个元素为JobPositionVO对象",
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.2 获取岗位详情
|
||||
|
||||
- **接口URL**:`/zsjy/position/{id}`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:position:query`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------|----------|----------|----------|----------|------------|
|
||||
| id | 岗位ID | path | 是 | long | 路径变量 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"data": "岗位详情数据,JobPositionVO对象",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.3 新增招聘岗位
|
||||
|
||||
- **接口URL**:`/zsjy/position`
|
||||
- **请求方式**:`POST`
|
||||
- **接口权限**:`zsjy:position:add`
|
||||
- **请求参数**:JobPositionVO对象(不含id、createBy、createTime、updateBy、updateTime字段)
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.4 修改招聘岗位
|
||||
|
||||
- **接口URL**:`/zsjy/position`
|
||||
- **请求方式**:`PUT`
|
||||
- **接口权限**:`zsjy:position:edit`
|
||||
- **请求参数**:JobPositionVO对象(必须包含id字段)
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.5 删除招聘岗位
|
||||
|
||||
- **接口URL**:`/zsjy/position/{ids}`
|
||||
- **请求方式**:`DELETE`
|
||||
- **接口权限**:`zsjy:position:remove`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------|-----------------------------|----------|----------|----------|-----------------------|
|
||||
| ids | 岗位ID,多个以逗号分隔 | path | 是 | string | 例如:1 或 1,2,3 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.6 获取企业下的岗位列表
|
||||
|
||||
- **接口URL**:`/zsjy/position/enterprise/{enterpriseId}`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:position:list`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|--------------|--------------|----------|----------|----------|-------------------------|
|
||||
| enterpriseId | 企业ID | path | 是 | long | 路径变量 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"data": "岗位列表数据,数组类型,每个元素为JobPositionVO对象",
|
||||
"msg": "消息提示"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 招聘海报管理接口
|
||||
|
||||
#### 2.3.1 生成企业招聘海报
|
||||
|
||||
- **接口URL**:`/zsjy/poster/enterprise/{enterpriseId}`
|
||||
- **请求方式**:`POST`
|
||||
- **接口权限**:`zsjy:poster:generate`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|--------------|----------|----------|----------|----------|------------|
|
||||
| enterpriseId | 企业ID | path | 是 | long | 路径变量 |
|
||||
|
||||
- **响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"posterPath": "生成的海报文件路径",
|
||||
"posterId": "海报记录ID"
|
||||
},
|
||||
"msg": "消息提示"
|
||||
"id": 1,
|
||||
"enterpriseName": "杭州某某科技有限公司",
|
||||
"industry": "01",
|
||||
"enterpriseIntro": "公司成立于2010年...",
|
||||
"contactName": "张三",
|
||||
"contactPhone": "13800138000",
|
||||
"officePhone": "0571-88888888",
|
||||
"email": "hr@example.com",
|
||||
"website": "https://www.example.com",
|
||||
"address": "浙江省杭州市xxx路xxx号",
|
||||
"status": "0",
|
||||
"jobList": [
|
||||
{
|
||||
"id": 1,
|
||||
"enterpriseId": 1,
|
||||
"positionName": "前端开发工程师",
|
||||
"requirements": "1. 熟练掌握Vue3\n2. 熟悉TypeScript",
|
||||
"benefits": "薪资范围:8000-15000元/月\n提供五险一金",
|
||||
"status": "0"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"enterpriseId": 1,
|
||||
"positionName": "后端开发工程师",
|
||||
"requirements": "1. 熟练掌握Java\n2. 熟悉Spring Boot",
|
||||
"benefits": "薪资范围:10000-20000元/月",
|
||||
"status": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.2 生成岗位招聘海报
|
||||
---
|
||||
|
||||
- **接口URL**:`/zsjy/poster/position/{positionId}`
|
||||
- **请求方式**:`POST`
|
||||
- **接口权限**:`zsjy:poster:generate`
|
||||
- **请求参数**:
|
||||
### 2.3 新增企业信息
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|------------|----------|----------|----------|----------|------------|
|
||||
| positionId | 岗位ID | path | 是 | long | 路径变量 |
|
||||
**接口地址**:`POST /zsjy/enterprise`
|
||||
|
||||
- **响应参数**:
|
||||
**权限标识**:`zsjy:enterprise:add`
|
||||
|
||||
**请求体**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "状态码,200表示成功",
|
||||
"enterpriseName": "杭州某某科技有限公司",
|
||||
"industry": "01",
|
||||
"enterpriseIntro": "公司成立于2010年,专注于软件开发...",
|
||||
"contactName": "张三",
|
||||
"contactPhone": "13800138000",
|
||||
"officePhone": "0571-88888888",
|
||||
"email": "hr@example.com",
|
||||
"website": "https://www.example.com",
|
||||
"address": "浙江省杭州市西湖区xxx路xxx号",
|
||||
"enterpriseScale": "01",
|
||||
"status": "0",
|
||||
"jobList": [
|
||||
{
|
||||
"positionName": "前端开发工程师",
|
||||
"requirements": "1. 熟练掌握Vue3\n2. 熟悉TypeScript",
|
||||
"benefits": "薪资范围:8000-15000元/月\n提供五险一金",
|
||||
"status": "0"
|
||||
},
|
||||
{
|
||||
"positionName": "后端开发工程师",
|
||||
"requirements": "1. 熟练掌握Java\n2. 熟悉Spring Boot",
|
||||
"benefits": "薪资范围:10000-20000元/月",
|
||||
"status": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4 修改企业信息
|
||||
|
||||
**接口地址**:`PUT /zsjy/enterprise`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:edit`
|
||||
|
||||
**说明**:编辑企业信息时,会先删除该企业下所有旧岗位,再批量插入新岗位列表。
|
||||
|
||||
**请求体**:同 2.3 新增
|
||||
|
||||
---
|
||||
|
||||
### 2.5 删除企业信息
|
||||
|
||||
**接口地址**:`DELETE /zsjy/enterprise/{ids}`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:remove`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| ids | Long[] | 是 | 企业ID数组 |
|
||||
|
||||
**说明**:删除企业时会同时删除关联的岗位信息。
|
||||
|
||||
---
|
||||
|
||||
### 2.6 导出企业信息
|
||||
|
||||
**接口地址**:`POST /zsjy/enterprise/export`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:export`
|
||||
|
||||
**请求参数**:同列表查询参数(用于条件导出)
|
||||
|
||||
**响应**:Excel文件流下载
|
||||
|
||||
---
|
||||
|
||||
### 2.7 下载导入模板
|
||||
|
||||
**接口地址**:`GET /zsjy/enterprise/importTemplate`
|
||||
|
||||
**权限标识**:无需权限
|
||||
|
||||
**响应**:Excel模板文件流下载,文件名:`enterprise_import_template.xlsx`
|
||||
|
||||
---
|
||||
|
||||
### 2.8 导入企业信息
|
||||
|
||||
**接口地址**:`POST /zsjy/enterprise/importData`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:import`
|
||||
|
||||
**请求方式**:`multipart/form-data`
|
||||
|
||||
**表单参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| file | File | 是 | Excel文件(.xlsx/.xls) |
|
||||
| updateSupport | Boolean | 否 | 是否支持更新(同名覆盖,默认false) |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "恭喜您,数据已全部导入成功!共 5 条,数据如下:<br/>1、企业 xxx 导入成功"
|
||||
}
|
||||
```
|
||||
|
||||
**导入规则**:
|
||||
- 企业名称唯一,重复时根据 `updateSupport` 决定是跳过还是覆盖
|
||||
- 岗位按模板列顺序解析,岗位名称非空则创建一条岗位记录
|
||||
|
||||
---
|
||||
|
||||
### 2.9 生成招聘简章PDF(单个)
|
||||
|
||||
**接口地址**:`POST /zsjy/enterprise/brochure/{enterpriseId}`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:brochure`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| enterpriseId | Long | 是 | 企业ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"posterPath": "生成的海报文件路径",
|
||||
"posterId": "海报记录ID"
|
||||
},
|
||||
"msg": "消息提示"
|
||||
"id": 1,
|
||||
"enterpriseId": 1,
|
||||
"enterpriseName": "杭州某某科技有限公司",
|
||||
"brochurePath": "/app/uploadPath/enterprise-brochure/2026/03/30/xxx.pdf",
|
||||
"fileName": "杭州某某科技有限公司_1743300000000.pdf",
|
||||
"fileSize": 102400,
|
||||
"generateBy": 1,
|
||||
"generateByName": "admin",
|
||||
"generateTime": "2026-03-30 10:00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.3 获取海报列表
|
||||
|
||||
- **接口URL**:`/zsjy/poster/list`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:poster:list`
|
||||
- **请求参数**:
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|--------------|--------------|----------|----------|----------|-----------------------------|
|
||||
| pageNum | 页码 | query | 是 | int | 默认为1 |
|
||||
| pageSize | 每页数量 | query | 是 | int | 默认为10 |
|
||||
| enterpriseId | 企业ID | query | 否 | long | 精确匹配 |
|
||||
| posterType | 海报类型 | query | 否 | string | 1企业海报 2岗位海报 |
|
||||
|
||||
- **响应参数**:
|
||||
**错误示例**(无岗位时):
|
||||
|
||||
```json
|
||||
{
|
||||
"total": "总记录数",
|
||||
"rows": "海报列表数据,数组类型,每个元素为RecruitmentPosterVO对象",
|
||||
"code": "状态码,200表示成功",
|
||||
"msg": "消息提示"
|
||||
"code": 500,
|
||||
"msg": "该企业下没有招聘岗位,无法生成简章"
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.4 导出海报为图片
|
||||
---
|
||||
|
||||
- **接口URL**:`/zsjy/poster/export/image/{id}`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:poster:export`
|
||||
- **请求参数**:
|
||||
### 2.10 批量生成招聘简章PDF
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------|----------|----------|----------|----------|------------|
|
||||
| id | 海报ID | path | 是 | long | 路径变量 |
|
||||
**接口地址**:`POST /zsjy/enterprise/brochure/batch/{enterpriseIds}`
|
||||
|
||||
- **响应参数**:图片文件流
|
||||
**权限标识**:`zsjy:enterprise:brochure`
|
||||
|
||||
#### 2.3.5 导出海报为PDF
|
||||
**路径参数**:
|
||||
|
||||
- **接口URL**:`/zsjy/poster/export/pdf/{id}`
|
||||
- **请求方式**:`GET`
|
||||
- **接口权限**:`zsjy:poster:export`
|
||||
- **请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| enterpriseIds | String | 是 | 企业ID列表,逗号分隔,如:`1,2,3` |
|
||||
|
||||
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 备注 |
|
||||
|----------|----------|----------|----------|----------|------------|
|
||||
| id | 海报ID | path | 是 | long | 路径变量 |
|
||||
**响应示例**:
|
||||
|
||||
- **响应参数**:PDF文件流
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"enterpriseId": 1,
|
||||
"enterpriseName": "杭州某某科技有限公司",
|
||||
"fileName": "杭州某某科技有限公司_xxx.pdf",
|
||||
"generateTime": "2026-03-30 10:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 数据字典
|
||||
---
|
||||
|
||||
### 3.1 企业规模 (zsjy_enterprise_scale)
|
||||
### 2.11 获取最新简章信息
|
||||
|
||||
| 字典值 | 字典名称 | 备注 |
|
||||
|--------|------------|------------|
|
||||
| 1-49 | 1-49人 | 小型企业 |
|
||||
| 50-99 | 50-99人 | |
|
||||
| 100-499| 100-499人 | 中型企业 |
|
||||
| 500-999| 500-999人 | |
|
||||
| 1000-4999| 1000-4999人 | 大型企业 |
|
||||
| 5000+ | 5000人以上 | 特大型企业 |
|
||||
**接口地址**:`GET /zsjy/enterprise/brochure/{enterpriseId}`
|
||||
|
||||
### 3.2 企业行业 (zsjy_enterprise_industry)
|
||||
**权限标识**:`zsjy:enterprise:query`
|
||||
|
||||
| 字典值 | 字典名称 |
|
||||
|--------|---------------------|
|
||||
| IT | 互联网/IT/电子/通信 |
|
||||
| FINANCE| 金融/银行/保险 |
|
||||
| EDUCATION | 教育/培训/科研 |
|
||||
| ESTATE | 房地产/建筑 |
|
||||
| MEDICAL| 医疗/健康 |
|
||||
| TRADE | 贸易/零售/物流 |
|
||||
| MANUFACTURING | 制造业 |
|
||||
| MEDIA | 文化/媒体/娱乐 |
|
||||
| ENERGY | 能源/环保 |
|
||||
| GOVERNMENT | 政府/非营利组织 |
|
||||
| OTHER | 其他行业 |
|
||||
**路径参数**:
|
||||
|
||||
### 3.3 岗位有效期类型 (zsjy_position_validity_type)
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| enterpriseId | Long | 是 | 企业ID |
|
||||
|
||||
| 字典值 | 字典名称 | 备注 |
|
||||
|--------|----------|----------------|
|
||||
| 1 | 永久有效 | 岗位永久有效 |
|
||||
| 0 | 日期区间 | 设置开始和结束日期|
|
||||
**响应示例**:同 2.9
|
||||
|
||||
## 4. 错误码
|
||||
---
|
||||
|
||||
| 错误码 | 描述 | 备注 |
|
||||
|--------|------------------------|----------------------------|
|
||||
| 200 | 操作成功 | |
|
||||
| 500 | 系统内部错误 | |
|
||||
| 401 | 未授权,请重新登录 | |
|
||||
| 403 | 权限不足,禁止访问 | |
|
||||
| 50001 | 企业信息不存在 | 查询或操作不存在的企业时 |
|
||||
| 50002 | 岗位信息不存在 | 查询或操作不存在的岗位时 |
|
||||
| 50003 | 海报生成失败 | 生成海报时发生错误 |
|
||||
| 50004 | 导出失败 | 导出海报为图片或PDF时失败 |
|
||||
| 50005 | 该企业下存在岗位,不能删除 | 删除有关联岗位的企业时 |
|
||||
### 2.12 下载招聘简章PDF(通过企业ID)
|
||||
|
||||
**接口地址**:`GET /zsjy/enterprise/brochure/download/{enterpriseId}`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:brochure`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| enterpriseId | Long | 是 | 企业ID |
|
||||
|
||||
**响应**:PDF文件流,Content-Type: `application/pdf`
|
||||
|
||||
**文件名**:`招聘简章_{企业名称}.pdf`
|
||||
|
||||
---
|
||||
|
||||
### 2.13 下载招聘简章PDF(通过记录ID)
|
||||
|
||||
**接口地址**:`GET /zsjy/enterprise/brochure/downloadById/{id}`
|
||||
|
||||
**权限标识**:`zsjy:enterprise:brochure`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 简章记录ID |
|
||||
|
||||
**响应**:PDF文件流
|
||||
|
||||
---
|
||||
|
||||
## 3. 招聘简章PDF版式说明
|
||||
|
||||
生成的招聘简章PDF每家企业一份,结构如下:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 企业名称(大标题) │ ← 蓝色背景标题区
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 企业简介 │ ← 企业简介区块
|
||||
│ 公司成立于2010年,专注于软件开发... │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 联系人 | 联系电话 | 办公电话 | 电子邮箱 │ ← 联系信息表格
|
||||
│ 企业地址:xxx路xxx号 │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 招聘岗位 │ ← 岗位标题
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 岗位1名称 │ ← 岗位1
|
||||
│ 岗位要求:... │
|
||||
│ 待遇:... │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 岗位2名称 │ ← 岗位2
|
||||
│ 岗位要求:... │
|
||||
│ 待遇:... │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
... 更多岗位 ...
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 联系人: 张三 | 电话: 13800138000 │ ← 页脚联系信息
|
||||
│ 邮箱: hr@example.com | 地址: xxx │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 权限标识清单
|
||||
|
||||
| 权限标识 | 说明 |
|
||||
|---------|------|
|
||||
| zsjy:enterprise:list | 列表查询 |
|
||||
| zsjy:enterprise:query | 查看详情 |
|
||||
| zsjy:enterprise:add | 新增 |
|
||||
| zsjy:enterprise:edit | 修改(含岗位增删) |
|
||||
| zsjy:enterprise:remove | 删除 |
|
||||
| zsjy:enterprise:export | 导出 |
|
||||
| zsjy:enterprise:import | 导入 |
|
||||
| zsjy:enterprise:brochure | 生成简章 / 下载 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据库表结构
|
||||
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| zsjy_enterprise | 企业信息表(改造:新增enterprise_intro、office_phone、website字段) |
|
||||
| zsjy_enterprise_job | 企业招聘岗位表(新建) |
|
||||
| zsjy_enterprise_brochure | 简章生成记录表(新建) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 变更记录
|
||||
|
||||
| 日期 | 版本 | 变更内容 |
|
||||
|------|------|----------|
|
||||
| 2023-07-05 | v1.0 | 初始版本(企业基本信息CRUD) |
|
||||
| 2026-03-30 | v2.0 | 重大改造:新增岗位子表、招聘简章PDF生成、Excel导入功能 |
|
||||
+102
-11
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* 企业信息管理 API
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-25
|
||||
* @date 2026-03-30
|
||||
*/
|
||||
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { EnterpriseVO, EnterpriseForm, EnterpriseQuery } from './types';
|
||||
import { EnterpriseVO, EnterpriseForm, EnterpriseQuery, EnterpriseJobForm, EnterpriseBrochureVO } from './types';
|
||||
|
||||
// TableDataInfo 接口定义
|
||||
export interface TableDataInfo<T> {
|
||||
@@ -19,7 +19,6 @@ export interface TableDataInfo<T> {
|
||||
/**
|
||||
* 查询企业信息列表
|
||||
* @param query 查询参数
|
||||
* @returns Promise<TableDataInfo<EnterpriseVO>>
|
||||
*/
|
||||
export function listEnterprise(query?: EnterpriseQuery): AxiosPromise<TableDataInfo<EnterpriseVO>> {
|
||||
return request({
|
||||
@@ -32,9 +31,8 @@ export function listEnterprise(query?: EnterpriseQuery): AxiosPromise<TableDataI
|
||||
/**
|
||||
* 查询企业信息详细
|
||||
* @param id 企业主键
|
||||
* @returns Promise<EnterpriseVO>
|
||||
*/
|
||||
export function getEnterprise(id: string | number): AxiosPromise<EnterpriseVO> {
|
||||
export function getEnterprise(id: string | number): AxiosPromise<any> {
|
||||
return request({
|
||||
url: `/zsjy/enterprise/${id}`,
|
||||
method: 'get'
|
||||
@@ -42,9 +40,8 @@ export function getEnterprise(id: string | number): AxiosPromise<EnterpriseVO> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增企业信息
|
||||
* 新增企业信息(含岗位)
|
||||
* @param data 企业信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
export function addEnterprise(data: EnterpriseForm): AxiosPromise<any> {
|
||||
return request({
|
||||
@@ -55,9 +52,8 @@ export function addEnterprise(data: EnterpriseForm): AxiosPromise<any> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改企业信息
|
||||
* 修改企业信息(含岗位)
|
||||
* @param data 企业信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
export function updateEnterprise(data: EnterpriseForm): AxiosPromise<any> {
|
||||
return request({
|
||||
@@ -70,7 +66,6 @@ export function updateEnterprise(data: EnterpriseForm): AxiosPromise<any> {
|
||||
/**
|
||||
* 删除企业信息
|
||||
* @param id 企业主键
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
export function delEnterprise(id: string | number | Array<string | number>): AxiosPromise<any> {
|
||||
return request({
|
||||
@@ -82,7 +77,6 @@ export function delEnterprise(id: string | number | Array<string | number>): Axi
|
||||
/**
|
||||
* 导出企业信息
|
||||
* @param query 查询参数
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
export function exportEnterprise(query: EnterpriseQuery): AxiosPromise<any> {
|
||||
return request({
|
||||
@@ -92,3 +86,100 @@ export function exportEnterprise(query: EnterpriseQuery): AxiosPromise<any> {
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业下拉选项
|
||||
*/
|
||||
export function listEnterpriseOptions(): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/zsjy/enterprise/options',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载导入模板
|
||||
*/
|
||||
export function getImportTemplate(): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/zsjy/enterprise/importTemplate',
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入企业信息
|
||||
* @param file Excel文件
|
||||
* @param updateSupport 是否支持更新(同名覆盖)
|
||||
*/
|
||||
export function importEnterprise(file: File, updateSupport: boolean = false): AxiosPromise<any> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('updateSupport', String(updateSupport));
|
||||
return request({
|
||||
url: '/zsjy/enterprise/importData',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成招聘简章PDF(单个)
|
||||
* @param enterpriseId 企业ID
|
||||
*/
|
||||
export function generateBrochure(enterpriseId: number | string): AxiosPromise<EnterpriseBrochureVO> {
|
||||
return request({
|
||||
url: `/zsjy/enterprise/brochure/${enterpriseId}`,
|
||||
method: 'post'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成招聘简章PDF
|
||||
* @param enterpriseIds 企业ID列表(逗号分隔)
|
||||
*/
|
||||
export function generateBrochureBatch(enterpriseIds: string): AxiosPromise<any> {
|
||||
return request({
|
||||
url: `/zsjy/enterprise/brochure/batch/${enterpriseIds}`,
|
||||
method: 'post'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新简章信息
|
||||
* @param enterpriseId 企业ID
|
||||
*/
|
||||
export function getBrochure(enterpriseId: number | string): AxiosPromise<any> {
|
||||
return request({
|
||||
url: `/zsjy/enterprise/brochure/${enterpriseId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载招聘简章PDF(通过企业ID)
|
||||
* @param enterpriseId 企业ID
|
||||
*/
|
||||
export function downloadBrochure(enterpriseId: number | string): AxiosPromise<any> {
|
||||
return request({
|
||||
url: `/zsjy/enterprise/brochure/download/${enterpriseId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载招聘简章PDF(通过简章记录ID)
|
||||
* @param id 简章记录ID
|
||||
*/
|
||||
export function downloadBrochureById(id: number | string): AxiosPromise<any> {
|
||||
return request({
|
||||
url: `/zsjy/enterprise/brochure/downloadById/${id}`,
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
+46
-1737
File diff suppressed because it is too large.
Load diff
@@ -248,17 +248,22 @@ service.interceptors.response.use(
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
// 通用下载方法
|
||||
export function download(url: string, params: any, fileName: string) {
|
||||
// 通用下载方法(支持GET和POST)
|
||||
export function download(url: string, params: any, fileName: string, method: string = 'post') {
|
||||
downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
|
||||
const isGet = method.toLowerCase() === 'get';
|
||||
// prettier-ignore
|
||||
return service.post(url, params, {
|
||||
transformRequest: [
|
||||
return service({
|
||||
url: url,
|
||||
method: method,
|
||||
params: isGet ? params : undefined,
|
||||
data: isGet ? undefined : params,
|
||||
transformRequest: isGet ? undefined : [
|
||||
(params: any) => {
|
||||
return tansParams(params);
|
||||
}
|
||||
],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
headers: isGet ? undefined : { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
responseType: 'blob',
|
||||
timeout: 1200000 // 导出接口单独设置20分钟超时
|
||||
}).then(async (resp: any) => {
|
||||
|
||||
@@ -63,6 +63,12 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExport" v-hasPermi="['zsjy:enterprise:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Upload" @click="showImportDialog = true" v-hasPermi="['zsjy:enterprise:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Document" :disabled="multiple" type="success" @click="handleGenerateBrochureBatch" v-hasPermi="['zsjy:enterprise:brochure']">批量生成简章</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -83,15 +89,18 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="联系人" align="center" prop="contactName" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="联系电话" align="center" prop="contactPhone" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="企业邮箱" align="center" prop="email" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="企业地址" align="center" prop="address" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="岗位数量" align="center" prop="jobCount" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag type="primary" effect="plain">{{ scope.row.jobCount || 0 }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="320" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button
|
||||
@@ -115,6 +124,17 @@
|
||||
v-hasPermi="['zsjy:enterprise:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="生成简章" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Document"
|
||||
size="default"
|
||||
style="font-size: 16px; margin-right: 8px"
|
||||
@click="handleGenerateBrochure(scope.row)"
|
||||
v-hasPermi="['zsjy:enterprise:brochure']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button
|
||||
link
|
||||
@@ -137,52 +157,74 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改企业信息对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="1100px" append-to-body class="enterprise-dialog">
|
||||
<el-form ref="enterpriseFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-divider content-position="left">企业基本信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业名称" prop="enterpriseName">
|
||||
<el-input v-model="form.enterpriseName" placeholder="请输入企业名称" />
|
||||
<el-input v-model="form.enterpriseName" placeholder="请输入企业名称" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industry">
|
||||
<el-select v-model="form.industry" placeholder="请选择所属行业" style="width: 100%">
|
||||
<el-select v-model="form.industry" placeholder="请选择所属行业" clearable style="width: 100%">
|
||||
<el-option v-for="dict in zsjy_enterprise_industry" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人姓名" prop="contactName">
|
||||
<el-input v-model="form.contactName" placeholder="请输入联系人姓名" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" maxlength="20" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="办公电话" prop="officePhone">
|
||||
<el-input v-model="form.officePhone" placeholder="请输入办公电话" maxlength="20" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="电子邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入电子邮箱" maxlength="100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="企业地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入企业地址" maxlength="255" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="企业简介" prop="enterpriseIntro">
|
||||
<el-input v-model="form.enterpriseIntro" type="textarea" placeholder="请输入企业简介(用于招聘简章展示)" :rows="4" maxlength="1500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业规模" prop="enterpriseScale">
|
||||
<el-select v-model="form.enterpriseScale" placeholder="请选择企业规模" style="width: 100%">
|
||||
<el-select v-model="form.enterpriseScale" placeholder="请选择企业规模" clearable style="width: 100%">
|
||||
<el-option v-for="dict in zsjy_enterprise_scale" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人姓名" prop="contactName">
|
||||
<el-input v-model="form.contactName" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入企业邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
@@ -192,30 +234,42 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="企业地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入企业地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 岗位信息区域 -->
|
||||
<el-divider content-position="left">
|
||||
招聘岗位信息
|
||||
<el-button type="primary" link icon="Plus" style="margin-left: 8px" @click="handleAddJob">添加岗位</el-button>
|
||||
</el-divider>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="主营业务" prop="mainBusiness">
|
||||
<el-input v-model="form.mainBusiness" type="textarea" placeholder="请输入主营业务" :autosize="{ minRows: 3, maxRows: 5 }" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注信息" :autosize="{ minRows: 2, maxRows: 4 }" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="job-table-container">
|
||||
<el-table :data="form.jobList" border style="width: 100%" size="small">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="岗位名称" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.positionName" placeholder="请输入岗位名称" maxlength="100" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="岗位要求" min-width="200">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.requirements" type="textarea" placeholder="请输入岗位要求" :rows="2" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="待遇" min-width="200">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row.benefits" type="textarea" placeholder="请输入待遇信息" :rows="2" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" icon="Delete" @click="handleRemoveJob(scope.$index)" :disabled="form.jobList.length <= 1">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="!form.jobList || form.jobList.length === 0" class="empty-job-tip">
|
||||
暂无岗位信息,点击上方「添加岗位」按钮新增
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
@@ -236,27 +290,135 @@
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人姓名">{{ viewForm.contactName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ viewForm.contactPhone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业邮箱">{{ viewForm.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办公电话">{{ viewForm.officePhone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电子邮箱">{{ viewForm.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业地址" span="2">{{ viewForm.address }}</el-descriptions-item>
|
||||
<el-descriptions-item label="主营业务" span="2">{{ viewForm.mainBusiness }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业简介" span="2">{{ viewForm.enterpriseIntro || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<dict-tag :options="sys_normal_disable" :value="viewForm.status" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ viewForm.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" span="2">{{ viewForm.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 岗位信息展示 -->
|
||||
<el-divider content-position="left">招聘岗位信息</el-divider>
|
||||
<el-table v-if="viewForm.jobList && viewForm.jobList.length > 0" :data="viewForm.jobList" border size="small">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="positionName" label="岗位名称" min-width="150" />
|
||||
<el-table-column prop="requirements" label="岗位要求" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="benefits" label="待遇" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-else description="暂无岗位信息" :image-size="60" />
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="success" icon="Document" @click="handleViewGenerateBrochure(viewForm)" v-hasPermi="['zsjy:enterprise:brochure']">生成简章</el-button>
|
||||
<el-button @click="viewDialog.visible = false">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog title="导入企业信息" v-model="showImportDialog" width="500px" append-to-body>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
drag
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:auto-upload="false"
|
||||
:headers="uploadHeaders"
|
||||
:action="importUrl"
|
||||
:disabled="importLoading"
|
||||
:on-progress="handleImportProgress"
|
||||
:on-success="handleImportSuccess"
|
||||
:on-error="handleImportError"
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将Excel文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
<el-checkbox v-model="importUpdateSupport" />是否更新已存在的企业数据
|
||||
<br />
|
||||
仅支持 .xlsx 和 .xls 格式,<el-link type="primary" :underline="false" @click="handleDownloadTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" :loading="importLoading" @click="submitImport">确 定</el-button>
|
||||
<el-button @click="showImportDialog = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 简章生成/预览对话框 -->
|
||||
<el-dialog
|
||||
title="招聘简章"
|
||||
v-model="brochureDialog.visible"
|
||||
width="900px"
|
||||
:close-on-click-modal="false"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="brochure-actions">
|
||||
<el-button type="success" icon="Download" @click="handleDownloadBrochure" :loading="brochureLoading">下载PDF</el-button>
|
||||
<el-button icon="Refresh" @click="handleReGenerateBrochure" :loading="brochureLoading">重新生成</el-button>
|
||||
</div>
|
||||
<div v-if="brochureDialog.brochureInfo" class="brochure-info">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="企业名称">{{ brochureDialog.brochureInfo.enterpriseName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="生成人">{{ brochureDialog.brochureInfo.generateByName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="生成时间" :span="2">{{ brochureDialog.brochureInfo.generateTime }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<!-- PDF预览区 -->
|
||||
<div class="pdf-preview-wrapper">
|
||||
<div v-if="brochureLoading && !brochureDialog.previewUrl" class="pdf-loading">
|
||||
<el-icon class="is-loading"><Loading /></el-icon>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
<iframe
|
||||
v-else-if="brochureDialog.previewUrl"
|
||||
:src="brochureDialog.previewUrl"
|
||||
class="pdf-iframe"
|
||||
title="招聘简章预览"
|
||||
/>
|
||||
<div v-else class="pdf-empty">暂无预览,请先生成简章</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="brochureDialog.visible = false">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Enterprise" lang="ts">
|
||||
import { listEnterprise, getEnterprise, delEnterprise, addEnterprise, updateEnterprise } from '@/api/zsjy/enterprise';
|
||||
import { EnterpriseVO, EnterpriseForm, EnterpriseQuery } from '@/api/zsjy/types';
|
||||
import {
|
||||
listEnterprise,
|
||||
getEnterprise,
|
||||
delEnterprise,
|
||||
addEnterprise,
|
||||
updateEnterprise,
|
||||
exportEnterprise,
|
||||
importEnterprise,
|
||||
getImportTemplate,
|
||||
generateBrochure,
|
||||
getBrochure,
|
||||
downloadBrochure
|
||||
} from '@/api/zsjy/enterprise';
|
||||
import { Loading } from '@element-plus/icons-vue';
|
||||
import { EnterpriseVO, EnterpriseJobForm } from '@/api/zsjy/types';
|
||||
import request from '@/utils/request';
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@@ -268,6 +430,28 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const importLoading = ref(false);
|
||||
const brochureLoading = ref(false);
|
||||
|
||||
// 导入相关
|
||||
const showImportDialog = ref(false);
|
||||
const importUpdateSupport = ref(false);
|
||||
const uploadRef = ref();
|
||||
const importUrl = computed(() => import.meta.env.VITE_APP_BASE_API + '/zsjy/enterprise/importData?updateSupport=' + importUpdateSupport.value);
|
||||
const uploadHeaders = globalHeaders();
|
||||
|
||||
// 简章相关
|
||||
const brochureDialog = reactive<{
|
||||
visible: boolean;
|
||||
enterpriseId: any;
|
||||
brochureInfo: any;
|
||||
previewUrl: string | null;
|
||||
}>({
|
||||
visible: false,
|
||||
enterpriseId: null,
|
||||
brochureInfo: null,
|
||||
previewUrl: null
|
||||
});
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const enterpriseFormRef = ref<ElFormInstance>();
|
||||
@@ -277,21 +461,25 @@ const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'))
|
||||
const { zsjy_enterprise_industry } = toRefs<any>(proxy?.useDict('zsjy_enterprise_industry'));
|
||||
const { zsjy_enterprise_scale } = toRefs<any>(proxy?.useDict('zsjy_enterprise_scale'));
|
||||
|
||||
const initFormData: EnterpriseForm = {
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
enterpriseName: '',
|
||||
industry: '',
|
||||
mainBusiness: '',
|
||||
enterpriseScale: '',
|
||||
enterpriseIntro: '',
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
officePhone: '',
|
||||
email: '',
|
||||
website: '',
|
||||
address: '',
|
||||
status: '0',
|
||||
remark: ''
|
||||
remark: '',
|
||||
jobList: [] as EnterpriseJobForm[]
|
||||
};
|
||||
|
||||
const data = reactive<PageData<EnterpriseForm, EnterpriseQuery>>({
|
||||
const data = reactive<PageData<typeof initFormData, any>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@@ -303,20 +491,16 @@ const data = reactive<PageData<EnterpriseForm, EnterpriseQuery>>({
|
||||
},
|
||||
rules: {
|
||||
enterpriseName: [{ required: true, message: '企业名称不能为空', trigger: 'blur' }],
|
||||
industry: [{ required: true, message: '所属行业不能为空', trigger: 'change' }],
|
||||
mainBusiness: [{ required: true, message: '主营业务不能为空', trigger: 'blur' }],
|
||||
enterpriseScale: [{ required: true, message: '企业规模不能为空', trigger: 'change' }],
|
||||
contactName: [{ required: true, message: '联系人姓名不能为空', trigger: 'blur' }],
|
||||
contactPhone: [
|
||||
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '企业邮箱不能为空', trigger: 'blur' },
|
||||
{ required: true, message: '电子邮箱不能为空', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
],
|
||||
address: [{ required: true, message: '企业地址不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
address: [{ required: true, message: '企业地址不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,10 +522,13 @@ const viewForm = ref<EnterpriseVO>({} as EnterpriseVO);
|
||||
/** 查询企业信息列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listEnterprise(queryParams.value);
|
||||
enterpriseList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
try {
|
||||
const res = await listEnterprise(queryParams.value);
|
||||
enterpriseList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
@@ -352,7 +539,7 @@ const cancel = () => {
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
form.value = { ...initFormData, jobList: [] };
|
||||
enterpriseFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
@@ -380,16 +567,28 @@ const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加企业信息';
|
||||
// 初始化至少一个空岗位行
|
||||
form.value.jobList = [{ positionName: '', requirements: '', benefits: '', status: '0' }];
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: EnterpriseVO) => {
|
||||
reset();
|
||||
const id = row?.id || ids.value[0];
|
||||
const res = await getEnterprise(id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改企业信息';
|
||||
loading.value = true;
|
||||
try {
|
||||
reset();
|
||||
const id = row?.id || ids.value[0];
|
||||
const res = await getEnterprise(id);
|
||||
// 显式设置 jobList,避免 Object.assign 后 jobList 被其他引用污染
|
||||
Object.assign(form.value, res.data, { jobList: res.data.jobList || [] });
|
||||
// 确保岗位列表不为空(至少有一行空岗位方便用户继续添加)
|
||||
if (!form.value.jobList || form.value.jobList.length === 0) {
|
||||
form.value.jobList = [{ positionName: '', requirements: '', benefits: '', status: '0' }];
|
||||
}
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改企业信息';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 查看详情 */
|
||||
@@ -399,27 +598,51 @@ const handleView = async (row: EnterpriseVO) => {
|
||||
viewDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 添加岗位 */
|
||||
const handleAddJob = () => {
|
||||
if (!form.value.jobList) {
|
||||
form.value.jobList = [];
|
||||
}
|
||||
form.value.jobList.push({ positionName: '', requirements: '', benefits: '', status: '0' });
|
||||
};
|
||||
|
||||
/** 删除岗位 */
|
||||
const handleRemoveJob = (index: number) => {
|
||||
if (form.value.jobList && form.value.jobList.length > 1) {
|
||||
form.value.jobList.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
enterpriseFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateEnterprise(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await addEnterprise(form.value).finally(() => (buttonLoading.value = false));
|
||||
try {
|
||||
// 过滤掉空岗位(岗位名称为空的不提交)
|
||||
const validJobList = (form.value.jobList || []).filter((job) => job.positionName && job.positionName.trim() !== '');
|
||||
const submitData = { ...form.value, jobList: validJobList };
|
||||
|
||||
if (form.value.id) {
|
||||
await updateEnterprise(submitData);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
await addEnterprise(submitData);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: EnterpriseVO) => {
|
||||
const idList = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除企业信息编号为"' + idList + '"的数据项?');
|
||||
const idList = row?.id ? [row.id] : ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除选中的企业信息?删除后将同时删除关联的岗位信息。');
|
||||
await delEnterprise(idList);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
@@ -427,13 +650,271 @@ const handleDelete = async (row?: EnterpriseVO) => {
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'zsjy/enterprise/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`enterprise_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
proxy?.download('zsjy/enterprise/export', { ...queryParams.value }, `enterprise_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
/** 下载导入模板 */
|
||||
const handleDownloadTemplate = () => {
|
||||
proxy?.download('zsjy/enterprise/importTemplate', {}, 'enterprise_import_template.xlsx', 'GET');
|
||||
};
|
||||
|
||||
/** 提交导入 */
|
||||
const submitImport = () => {
|
||||
if (!uploadRef.value) {
|
||||
proxy?.$modal.msgError('上传组件未初始化');
|
||||
return;
|
||||
}
|
||||
importLoading.value = true;
|
||||
uploadRef.value?.submit();
|
||||
};
|
||||
|
||||
/** 导入文件上传中 */
|
||||
const handleImportProgress = () => {
|
||||
importLoading.value = true;
|
||||
};
|
||||
|
||||
/** 导入文件上传成功 */
|
||||
const handleImportSuccess = (response: any) => {
|
||||
importLoading.value = false;
|
||||
showImportDialog.value = false;
|
||||
proxy?.$modal.msgSuccess(response.msg || '导入成功');
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 导入文件上传失败 */
|
||||
const handleImportError = (err: any) => {
|
||||
importLoading.value = false;
|
||||
console.error('导入失败:', err);
|
||||
proxy?.$modal.msgError(err?.message || '导入失败');
|
||||
};
|
||||
|
||||
/** 生成简章(单个) */
|
||||
const handleGenerateBrochure = async (row: EnterpriseVO) => {
|
||||
brochureLoading.value = true;
|
||||
// 生成前清理旧预览
|
||||
if (brochureDialog.previewUrl) {
|
||||
window.URL.revokeObjectURL(brochureDialog.previewUrl);
|
||||
brochureDialog.previewUrl = null;
|
||||
}
|
||||
try {
|
||||
const res = await generateBrochure(row.id);
|
||||
brochureDialog.enterpriseId = row.id;
|
||||
brochureDialog.brochureInfo = res.data;
|
||||
brochureDialog.visible = true;
|
||||
// 生成成功后自动加载预览
|
||||
await loadBrochurePreview(row.id);
|
||||
proxy?.$modal.msgSuccess('简章生成成功');
|
||||
} catch (err: any) {
|
||||
proxy?.$modal.msgError(err?.msg || '生成失败');
|
||||
} finally {
|
||||
brochureLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 批量生成简章 */
|
||||
const handleGenerateBrochureBatch = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
proxy?.$modal.msgWarning('请先选择要生成简章的企业');
|
||||
return;
|
||||
}
|
||||
await proxy?.$modal.confirm(`确认要批量生成 ${ids.value.length} 家企业的招聘简章吗?\n生成后将下载一个包含 ${ids.value.length} 页的多页PDF文件。`);
|
||||
brochureLoading.value = true;
|
||||
proxy?.$modal.loading('正在生成多页PDF,请稍候...');
|
||||
try {
|
||||
const response: any = await request({
|
||||
url: '/zsjy/enterprise/brochure/batch/download',
|
||||
method: 'post',
|
||||
data: ids.value,
|
||||
responseType: 'blob',
|
||||
timeout: 300000
|
||||
});
|
||||
|
||||
proxy?.$modal.closeLoading();
|
||||
|
||||
// 处理响应
|
||||
if (!response) {
|
||||
throw new Error('下载响应为空');
|
||||
}
|
||||
|
||||
// 检查是否是 Blob 对象
|
||||
const blob = response instanceof Blob ? response : null;
|
||||
if (!blob) {
|
||||
throw new Error('下载响应格式错误');
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
if (blob.size === 0) {
|
||||
throw new Error('下载文件为空');
|
||||
}
|
||||
|
||||
// 检查是否是 JSON 错误响应
|
||||
if (blob.type === 'application/json' || blob.type === 'text/plain') {
|
||||
const text = await blob.text();
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
throw new Error(json.msg || '下载失败');
|
||||
} catch (e) {
|
||||
if (e.message !== '下载失败') {
|
||||
throw new Error('下载失败,请重试');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `企业招聘简章_${new Date().getTime()}.pdf`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
proxy?.$modal.msgSuccess('批量生成成功,共下载 ' + ids.value.length + ' 页');
|
||||
} catch (err: any) {
|
||||
console.error('批量生成失败:', err);
|
||||
proxy?.$modal.msgError(err?.message || err?.msg || '批量生成失败');
|
||||
} finally {
|
||||
brochureLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 查看详情时生成简章 */
|
||||
const handleViewGenerateBrochure = async (row: EnterpriseVO) => {
|
||||
await handleGenerateBrochure(row);
|
||||
viewDialog.visible = false;
|
||||
};
|
||||
|
||||
/** 下载PDF */
|
||||
const handleDownloadBrochure = async () => {
|
||||
if (!brochureDialog.enterpriseId) return;
|
||||
|
||||
// 显示下载加载状态
|
||||
const loadingInstance = ElLoading.service({ text: '正在下载,请稍候...', background: 'rgba(0, 0, 0, 0.7)' });
|
||||
|
||||
try {
|
||||
// 使用API中已有的downloadBrochure函数,确保认证正确
|
||||
const blob: any = await downloadBrochure(brochureDialog.enterpriseId);
|
||||
|
||||
// 检查响应
|
||||
if (!blob) {
|
||||
throw new Error('下载响应为空');
|
||||
}
|
||||
|
||||
// 检查是否是 Blob 对象
|
||||
if (!(blob instanceof Blob)) {
|
||||
throw new Error('下载响应格式错误');
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
if (blob.size === 0) {
|
||||
throw new Error('下载文件为空');
|
||||
}
|
||||
|
||||
// 检查是否是 JSON 错误响应
|
||||
if (blob.type === 'application/json' || (blob.type === 'text/plain')) {
|
||||
const text = await blob.text();
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
throw new Error(json.msg || '下载失败');
|
||||
} catch {
|
||||
throw new Error('下载失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `招聘简章_${brochureDialog.brochureInfo?.enterpriseName || '未知企业'}.pdf`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
proxy?.$modal.msgSuccess('下载成功');
|
||||
} catch (err: any) {
|
||||
console.error('下载PDF失败:', err);
|
||||
proxy?.$modal.msgError(err?.message || '下载失败');
|
||||
} finally {
|
||||
loadingInstance.close();
|
||||
}
|
||||
};
|
||||
|
||||
/** 加载简章预览 */
|
||||
const loadBrochurePreview = async (enterpriseId: any) => {
|
||||
if (!enterpriseId) return;
|
||||
brochureLoading.value = true;
|
||||
try {
|
||||
console.log('开始加载预览,企业ID:', enterpriseId);
|
||||
const response = await downloadBrochure(enterpriseId);
|
||||
|
||||
console.log('预览响应类型:', typeof response, 'response:', response);
|
||||
|
||||
// 检查响应
|
||||
if (!response) {
|
||||
console.error('预览响应为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是 Blob 对象
|
||||
if (!(response instanceof Blob)) {
|
||||
console.error('预览响应不是Blob对象:', typeof response);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Blob大小:', response.size, '类型:', response.type);
|
||||
|
||||
// 检查文件大小
|
||||
if (response.size === 0) {
|
||||
console.error('预览文件为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是 JSON 错误响应
|
||||
if (response.type === 'application/json' || response.type === 'text/plain') {
|
||||
const text = await response.text();
|
||||
console.error('服务端返回了JSON错误:', text);
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
console.error('预览加载失败,服务端返回错误:', json.msg || json.message);
|
||||
} catch {
|
||||
console.error('预览加载失败,未知错误');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理旧的预览URL
|
||||
if (brochureDialog.previewUrl) {
|
||||
window.URL.revokeObjectURL(brochureDialog.previewUrl);
|
||||
}
|
||||
|
||||
// 创建新的预览URL
|
||||
brochureDialog.previewUrl = window.URL.createObjectURL(response);
|
||||
console.log('预览加载成功');
|
||||
} catch (err: any) {
|
||||
console.error('预览加载失败:', err);
|
||||
// 显示错误消息
|
||||
proxy?.$modal.msgError('预览加载失败: ' + (err?.message || err?.msg || '未知错误'));
|
||||
} finally {
|
||||
brochureLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 重新生成 */
|
||||
const handleReGenerateBrochure = async () => {
|
||||
if (!brochureDialog.enterpriseId) return;
|
||||
brochureLoading.value = true;
|
||||
try {
|
||||
const res = await generateBrochure(brochureDialog.enterpriseId);
|
||||
brochureDialog.brochureInfo = res.data;
|
||||
await loadBrochurePreview(brochureDialog.enterpriseId);
|
||||
proxy?.$modal.msgSuccess('重新生成成功');
|
||||
} catch (err: any) {
|
||||
proxy?.$modal.msgError(err?.msg || '重新生成失败');
|
||||
} finally {
|
||||
brochureLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
@@ -442,7 +923,6 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 继承后勤管理的样式
|
||||
.page-container {
|
||||
.search-btn-container {
|
||||
text-align: right;
|
||||
@@ -473,26 +953,54 @@ onMounted(() => {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// 表格适应性样式
|
||||
.pdf-preview-wrapper {
|
||||
margin-top: 15px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #f5f7fa;
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.pdf-loading,
|
||||
.pdf-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #909399;
|
||||
padding: 40px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.pdf-iframe {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
.el-table__cell {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式表格
|
||||
:deep(.el-table__body-wrapper) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
// 表格列宽自适应
|
||||
.el-table {
|
||||
width: 100%;
|
||||
table-layout: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 描述列表样式优化
|
||||
:deep(.el-descriptions) {
|
||||
.el-descriptions__label {
|
||||
font-weight: 600;
|
||||
@@ -504,6 +1012,7 @@ onMounted(() => {
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.search-btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -513,4 +1022,55 @@ onMounted(() => {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.enterprise-dialog {
|
||||
:deep(.el-dialog__body) {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.job-table-container {
|
||||
margin-top: 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
|
||||
.empty-job-tip {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.brochure-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.brochure-info {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
text-align: center;
|
||||
|
||||
.el-icon-upload {
|
||||
font-size: 67px;
|
||||
color: #409eff;
|
||||
margin: 20px 0 10px;
|
||||
}
|
||||
|
||||
.el-upload__text {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
|
||||
em {
|
||||
color: #409eff;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -175,7 +175,30 @@
|
||||
<el-radio v-for="dict in zsjy_poster_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="关联岗位" prop="positionId" v-if="form.posterType === '2'">
|
||||
<!-- 新增岗位海报时,支持岗位多选 -->
|
||||
<el-form-item
|
||||
v-if="form.posterType === '2' && !form.id"
|
||||
label="关联岗位"
|
||||
prop="positionIdList"
|
||||
>
|
||||
<el-select
|
||||
v-model="form.positionIdList"
|
||||
placeholder="请选择关联岗位(可多选)"
|
||||
style="width: 100%"
|
||||
:disabled="!form.enterpriseId"
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
>
|
||||
<el-option v-for="item in formPositionOptions" :key="item.id" :label="item.positionName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 编辑岗位海报时,保持单选,避免修改已生成数据的关联关系过于复杂 -->
|
||||
<el-form-item
|
||||
v-else-if="form.posterType === '2' && !!form.id"
|
||||
label="关联岗位"
|
||||
prop="positionId"
|
||||
>
|
||||
<el-select v-model="form.positionId" placeholder="请选择关联岗位" style="width: 100%" :disabled="!form.enterpriseId">
|
||||
<el-option v-for="item in formPositionOptions" :key="item.id" :label="item.positionName" :value="item.id" />
|
||||
</el-select>
|
||||
@@ -199,28 +222,17 @@
|
||||
<!-- 预览对话框 -->
|
||||
<el-dialog title="海报预览" v-model="previewOpen" width="900px" append-to-body>
|
||||
<div v-if="previewOpen && previewData.posterPath" class="poster-preview-container">
|
||||
<!-- 海报图片预览 -->
|
||||
<div class="poster-image-section">
|
||||
<image-preview :src="previewData.posterPath" :width="400" :height="600" :preview="true" style="margin: 0 auto" />
|
||||
</div>
|
||||
|
||||
<!-- 海报详情信息 -->
|
||||
<div class="poster-info-section">
|
||||
<el-descriptions title="海报详情" :column="2" border>
|
||||
<el-descriptions-item label="海报类型">
|
||||
<dict-tag :options="zsjy_poster_type" :value="previewData.posterType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所属企业">
|
||||
{{ enterpriseDetail.enterpriseName || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联岗位" v-if="previewData.posterType === '2'">
|
||||
{{ positionDetail.positionName || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ previewData.createTime || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<!-- 使用 PosterPreview 组件生成带企业/岗位信息的海报,并支持导出 -->
|
||||
<PosterPreview
|
||||
:posterData="{
|
||||
posterPath: previewData.posterPath,
|
||||
enterpriseName: enterpriseDetail.enterpriseName || previewData.enterpriseName || '',
|
||||
positionName: positionDetail.positionName || previewData.positionName || '',
|
||||
posterType: previewData.posterType
|
||||
}"
|
||||
:enterpriseDetail="enterpriseDetail"
|
||||
:positionDetail="positionDetail"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 无海报图片提示 -->
|
||||
@@ -256,6 +268,7 @@ import { listEnterpriseOptions } from '@/api/zsjy/jobPosition';
|
||||
import { getEnterprise } from '@/api/zsjy/enterprise';
|
||||
import { getJobPosition } from '@/api/zsjy/jobPosition';
|
||||
import { RecruitmentPosterVO, RecruitmentPosterForm, RecruitmentPosterQuery } from '@/api/zsjy/types';
|
||||
import PosterPreview from '@/components/PosterPreview/index.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@@ -295,10 +308,11 @@ const queryParams = ref<RecruitmentPosterQuery>({
|
||||
});
|
||||
|
||||
// 表单参数
|
||||
const form = ref<RecruitmentPosterForm>({
|
||||
const form = ref<RecruitmentPosterForm & { positionIdList?: Array<number | string> }>({
|
||||
id: undefined,
|
||||
enterpriseId: '',
|
||||
positionId: undefined,
|
||||
positionIdList: [],
|
||||
posterPath: '',
|
||||
posterType: '1'
|
||||
});
|
||||
@@ -307,8 +321,17 @@ const form = ref<RecruitmentPosterForm>({
|
||||
const rules = {
|
||||
enterpriseId: [{ required: true, message: '请选择所属企业', trigger: 'change' }],
|
||||
posterType: [{ required: true, message: '请选择海报类型', trigger: 'change' }],
|
||||
posterPath: [{ required: true, message: '请上传海报图片', trigger: 'change' }]
|
||||
};
|
||||
posterPath: [{ required: true, message: '请上传海报图片', trigger: 'change' }],
|
||||
// 仅在新增岗位海报且岗位多选时生效
|
||||
positionIdList: [
|
||||
{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请选择至少一个关联岗位',
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
} as const;
|
||||
|
||||
// 预览数据
|
||||
const previewOpen = ref(false);
|
||||
@@ -378,6 +401,7 @@ const handleEnterpriseChange = (val: number | string) => {
|
||||
/** 表单企业选择改变 */
|
||||
const handleFormEnterpriseChange = (val: number | string) => {
|
||||
form.value.positionId = undefined;
|
||||
form.value.positionIdList = [];
|
||||
if (val) {
|
||||
getFormPositionOptions(val);
|
||||
} else {
|
||||
@@ -389,6 +413,7 @@ const handleFormEnterpriseChange = (val: number | string) => {
|
||||
const handlePosterTypeChange = (val: string) => {
|
||||
if (val === '1') {
|
||||
form.value.positionId = undefined;
|
||||
form.value.positionIdList = [];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -460,17 +485,48 @@ const handlePreview = async (row: RecruitmentPosterVO) => {
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
posterFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
await updateRecruitmentPoster(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
await addRecruitmentPoster(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 编辑模式:保持原有单条更新逻辑
|
||||
if (form.value.id) {
|
||||
await updateRecruitmentPoster(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
open.value = false;
|
||||
getList();
|
||||
return;
|
||||
}
|
||||
|
||||
// 新增岗位海报且选择了多个岗位时,批量生成多条记录
|
||||
if (
|
||||
form.value.posterType === '2' &&
|
||||
Array.isArray(form.value.positionIdList) &&
|
||||
form.value.positionIdList.length > 0
|
||||
) {
|
||||
const { positionIdList, ...baseForm } = form.value as RecruitmentPosterForm & {
|
||||
positionIdList: Array<number | string>;
|
||||
};
|
||||
|
||||
const requests = positionIdList.map((pid) =>
|
||||
addRecruitmentPoster({
|
||||
...baseForm,
|
||||
positionId: pid
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(requests);
|
||||
proxy?.$modal.msgSuccess(`新增成功(共生成 ${positionIdList.length} 条岗位海报)`);
|
||||
open.value = false;
|
||||
getList();
|
||||
return;
|
||||
}
|
||||
|
||||
// 其他情况(企业海报或单个岗位海报),按原有逻辑新增一条记录
|
||||
await addRecruitmentPoster(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -502,6 +558,7 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
enterpriseId: '',
|
||||
positionId: undefined,
|
||||
positionIdList: [],
|
||||
posterPath: '',
|
||||
posterType: '1'
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ import path from 'path';
|
||||
export default (viteEnv: any, isBuild = false): [] => {
|
||||
const vitePlugins: any = [];
|
||||
vitePlugins.push(vue());
|
||||
vitePlugins.push(vueDevTools());
|
||||
// vitePlugins.push(vueDevTools()); // 禁用以解决localStorage兼容性问题
|
||||
vitePlugins.push(createUnoCss());
|
||||
vitePlugins.push(createAutoImport(path));
|
||||
vitePlugins.push(createComponents(path));
|
||||
|
||||
Reference in new issue
Block a user