diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774849435858.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774849435858.pdf new file mode 100644 index 00000000..07502093 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774849435858.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774850848936.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774850848936.pdf new file mode 100644 index 00000000..f0bc1904 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774850848936.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774851919249.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774851919249.pdf new file mode 100644 index 00000000..f9cae0ba Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774851919249.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774851924203.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774851924203.pdf new file mode 100644 index 00000000..e0f08309 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774851924203.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774853049903.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774853049903.pdf new file mode 100644 index 00000000..5f941a96 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774853049903.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774853077245.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774853077245.pdf new file mode 100644 index 00000000..8c298c7e Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774853077245.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774855767554.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774855767554.pdf new file mode 100644 index 00000000..b2fccce2 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774855767554.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774859781493.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774859781493.pdf new file mode 100644 index 00000000..6ceaae6c Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774859781493.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774866493064.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774866493064.pdf new file mode 100644 index 00000000..7edb9aed Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774866493064.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774867672726.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774867672726.pdf new file mode 100644 index 00000000..b01390b8 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/30/科大讯飞_1774867672726.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774914364432.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774914364432.pdf new file mode 100644 index 00000000..a748c571 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774914364432.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915493090.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915493090.pdf new file mode 100644 index 00000000..fb79e29b Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915493090.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915532226.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915532226.pdf new file mode 100644 index 00000000..7c8bed8c Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915532226.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915743421.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915743421.pdf new file mode 100644 index 00000000..20eebe92 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774915743421.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774928212422.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774928212422.pdf new file mode 100644 index 00000000..a1e6a3e2 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774928212422.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774928217891.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774928217891.pdf new file mode 100644 index 00000000..63219f60 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774928217891.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774932565719.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774932565719.pdf new file mode 100644 index 00000000..d619e27e Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774932565719.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774932570289.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774932570289.pdf new file mode 100644 index 00000000..1e13cb54 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774932570289.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774933408709.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774933408709.pdf new file mode 100644 index 00000000..2b3a8c81 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774933408709.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774933835914.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774933835914.pdf new file mode 100644 index 00000000..f96de1f9 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774933835914.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774934307310.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774934307310.pdf new file mode 100644 index 00000000..b5b5c62b Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774934307310.pdf differ diff --git a/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774937223681.pdf b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774937223681.pdf new file mode 100644 index 00000000..dd19ae17 Binary files /dev/null and b/backend/ruoyi-admin/uploadPath/enterprise-brochure/2026/03/31/科大讯飞_1774937223681.pdf differ diff --git a/backend/ruoyi-modules/ruoyi-zsjy/pom.xml b/backend/ruoyi-modules/ruoyi-zsjy/pom.xml index aed2e7cc..9fbea31f 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/pom.xml +++ b/backend/ruoyi-modules/ruoyi-zsjy/pom.xml @@ -110,6 +110,20 @@ ruoyi-xg + + + com.itextpdf + itextpdf + 5.5.13.3 + + + + + com.itextpdf + itext-asian + 5.2.0 + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyEnterpriseController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyEnterpriseController.java index 15d70490..959563a2 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyEnterpriseController.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyEnterpriseController.java @@ -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 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 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 getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { - return R.ok(enterpriseService.queryById(id)); + public R 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 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 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 remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + public R remove(@PathVariable Long[] ids) { return toAjax(enterpriseService.deleteWithValidByIds(Arrays.asList(ids), true)); } /** * 获取企业下拉选项 */ - // @SaCheckPermission("zsjy:enterprise:list") @GetMapping("/options") public R> 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 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 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 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> generateBrochureBatch(@PathVariable String enterpriseIds) { + List 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 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); + } + } + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterprise.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterprise.java index a1e73996..da008a71 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterprise.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterprise.java @@ -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; + /** * 企业地址 */ diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterpriseBrochure.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterpriseBrochure.java new file mode 100644 index 00000000..240b2cdf --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterpriseBrochure.java @@ -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; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterpriseJob.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterpriseJob.java new file mode 100644 index 00000000..4fb1691f --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyEnterpriseJob.java @@ -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; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseBo.java index fa5fa4a4..f88f925a 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseBo.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseBo.java @@ -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 jobList; } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseJobBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseJobBo.java new file mode 100644 index 00000000..c1cc1c75 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyEnterpriseJobBo.java @@ -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; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseBrochureVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseBrochureVo.java new file mode 100644 index 00000000..20e7ac2b --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseBrochureVo.java @@ -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; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseImportVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseImportVo.java new file mode 100644 index 00000000..3b34297a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseImportVo.java @@ -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; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseJobVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseJobVo.java new file mode 100644 index 00000000..c88e4257 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseJobVo.java @@ -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; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseVo.java index 41cd7980..f91596e7 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseVo.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyEnterpriseVo.java @@ -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 jobList; + + /** + * 岗位数量(列表展示用) + */ + private Integer jobCount; } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/listener/ZsjyEnterpriseImportListener.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/listener/ZsjyEnterpriseImportListener.java new file mode 100644 index 00000000..991b7c4e --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/listener/ZsjyEnterpriseImportListener.java @@ -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 + implements ExcelListener { + + 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("
").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("
").append(successNum).append("、企业 ").append(bo.getEnterpriseName()) + .append(" 导入成功"); + } catch (Exception e) { + failureNum++; + // 安全处理null情况 + String enterpriseName = importVo.getEnterpriseName() != null ? importVo.getEnterpriseName() : "未知企业"; + String msg = "
" + 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 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 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 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 getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseBrochureMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseBrochureMapper.java new file mode 100644 index 00000000..d22998ba --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseBrochureMapper.java @@ -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 { + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseJobMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseJobMapper.java new file mode 100644 index 00000000..b0c38d79 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseJobMapper.java @@ -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 { + + /** + * 按企业ID查询岗位列表(用于简章展示) + */ + List selectVoListByEnterpriseId(Long enterpriseId); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseMapper.java index cf184e78..70fc30bc 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseMapper.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyEnterpriseMapper.java @@ -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 { + /** + * 查询企业详情(含岗位列表,使用XML自定义resultMap) + * + * @param id 企业ID + * @return 企业信息(含岗位列表) + */ + ZsjyEnterpriseVo selectVoWithJobsById(Long id); + + /** + * 分页查询企业列表(包含岗位数量,job_count通过子查询计算) + * + * @param page 分页参数 + * @param bo 查询条件 + * @return 企业分页列表 + */ + Page selectVoPageWithJobCount(IPage page, @Param("bo") ZsjyEnterpriseBo bo); + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseBrochureService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseBrochureService.java new file mode 100644 index 00000000..d06f77e2 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseBrochureService.java @@ -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 generateBrochureBatch(List 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 enterpriseIds, HttpServletResponse response); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseJobService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseJobService.java new file mode 100644 index 00000000..367531e8 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseJobService.java @@ -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 queryListByEnterpriseId(Long enterpriseId); + + /** + * 查询岗位列表 + */ + List queryList(ZsjyEnterpriseJobBo bo); + + /** + * 新增岗位 + */ + Boolean insertByBo(ZsjyEnterpriseJobBo bo); + + /** + * 批量新增岗位(用于企业新增/编辑时批量插入岗位) + * + * @param enterpriseId 企业ID + * @param jobList 岗位列表 + * @return 结果 + */ + Boolean insertBatch(Long enterpriseId, List jobList); + + /** + * 修改岗位 + */ + Boolean updateByBo(ZsjyEnterpriseJobBo bo); + + /** + * 删除岗位 + */ + Boolean deleteWithValidByIds(Collection ids); + + /** + * 按企业ID删除岗位(用于企业删除时级联删除) + */ + Boolean deleteByEnterpriseIds(Collection enterpriseIds); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseService.java index f939e844..dc5277fd 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseService.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyEnterpriseService.java @@ -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 queryPageList(ZsjyEnterpriseBo bo, PageQuery pageQuery); /** * 查询企业信息列表 - * - * @param bo 企业信息 - * @return 企业信息集合 */ List 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 ids, Boolean isValid); + /** + * 根据企业名称查询(用于导入时唯一性校验) + */ + ZsjyEnterpriseVo queryByName(String enterpriseName); + + /** + * 批量导入企业及岗位信息 + * + * @param boList 企业及岗位列表 + * @param isUpdateSupport 是否支持更新(同名覆盖) + * @param operName 操作人 + * @return 导入结果 + */ + String importData(List boList, Boolean isUpdateSupport, String operName); + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseBrochureServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseBrochureServiceImpl.java new file mode 100644 index 00000000..27be0076 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseBrochureServiceImpl.java @@ -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 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 generateBrochureBatch(java.util.List enterpriseIds) { + java.util.List 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 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 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; + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseJobServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseJobServiceImpl.java new file mode 100644 index 00000000..ad23c1de --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseJobServiceImpl.java @@ -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 queryListByEnterpriseId(Long enterpriseId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(ZsjyEnterpriseJob::getEnterpriseId, enterpriseId); + lqw.eq(ZsjyEnterpriseJob::getStatus, "0"); + lqw.orderByAsc(ZsjyEnterpriseJob::getCreateTime); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryList(ZsjyEnterpriseJobBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + lqw.orderByDesc(ZsjyEnterpriseJob::getCreateTime); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(ZsjyEnterpriseJobBo bo) { + LambdaQueryWrapper 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 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 ids) { + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteByEnterpriseIds(Collection enterpriseIds) { + if (enterpriseIds == null || enterpriseIds.isEmpty()) { + return true; + } + for (Long enterpriseId : enterpriseIds) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZsjyEnterpriseJob::getEnterpriseId, enterpriseId); + baseMapper.delete(wrapper); + } + return true; + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseServiceImpl.java index 296735ee..83db674a 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyEnterpriseServiceImpl.java @@ -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 queryPageList(ZsjyEnterpriseBo bo, PageQuery pageQuery) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = pageQuery.build(); + Page result = baseMapper.selectVoPageWithJobCount(page, bo); return TableDataInfo.build(result); } - /** - * 查询企业信息列表 - * - * @param bo 企业信息 - * @return 企业信息 - */ @Override public List queryList(ZsjyEnterpriseBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } + @Override + public ZsjyEnterpriseVo queryWithJobs(Long id) { + ZsjyEnterpriseVo vo = baseMapper.selectVoById(id); + if (vo != null) { + LambdaQueryWrapper jobWrapper = Wrappers.lambdaQuery(); + jobWrapper.eq(ZsjyEnterpriseJob::getEnterpriseId, id); + jobWrapper.eq(ZsjyEnterpriseJob::getStatus, "0"); + jobWrapper.orderByAsc(ZsjyEnterpriseJob::getCreateTime); + List jobs = jobMapper.selectVoList(jobWrapper); + vo.setJobList(jobs); + } + return vo; + } + private LambdaQueryWrapper buildQueryWrapper(ZsjyEnterpriseBo bo) { - Map params = bo.getParams(); LambdaQueryWrapper 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 ids, Boolean isValid) { if (isValid) { - // 校验是否存在关联的岗位 for (Long id : ids) { - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(ZsjyJobPosition.class) - .eq(ZsjyJobPosition::getEnterpriseId, id); - long count = jobPositionMapper.selectCount(wrapper); - if (count > 0) { + List 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 lqw = Wrappers.lambdaQuery(); + lqw.eq(ZsjyEnterprise::getEnterpriseName, enterpriseName); + return baseMapper.selectVoOne(lqw); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String importData(List 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("
").append(successNum).append("、企业 ").append(bo.getEnterpriseName()).append(" 导入成功"); + } else if (isUpdateSupport) { + bo.setId(exist.getId()); + updateByBo(bo); + successNum++; + successMsg.append("
").append(successNum).append("、企业 ").append(bo.getEnterpriseName()).append(" 更新成功"); + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、企业 ").append(bo.getEnterpriseName()).append(" 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + 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(); + } } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/brochure/recruitment-brochure-bg.png b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/brochure/recruitment-brochure-bg.png new file mode 100644 index 00000000..0e42a69a Binary files /dev/null and b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/brochure/recruitment-brochure-bg.png differ diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseBrochureMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseBrochureMapper.xml new file mode 100644 index 00000000..cdb98ef6 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseBrochureMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseJobMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseJobMapper.xml new file mode 100644 index 00000000..ae7ab742 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseJobMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseMapper.xml index 803c44e4..b23cc546 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseMapper.xml +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/zsjy/ZsjyEnterpriseMapper.xml @@ -12,9 +12,12 @@ + + + @@ -22,6 +25,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -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' + + 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' + + + + + + + + diff --git a/docs/开发文档/API/API文档总览.md b/docs/开发文档/API/API文档总览.md index 736e313a..226d8b85 100644 --- a/docs/开发文档/API/API文档总览.md +++ b/docs/开发文档/API/API文档总览.md @@ -73,6 +73,8 @@ ## 企业信息管理API +[企业信息管理API](./企业信息管理API.md) - 包含企业基本信息管理、招聘岗位管理(含子表CRUD)、招聘简章PDF生成(单个/批量)、Excel导入功能(支持企业+岗位一体化导入)。 +**重大更新(2026-03-30)**:企业信息新增企业简介、办公电话、官网字段;整合岗位管理;新增简章PDF生成和导入功能。 [统计报表API](./统计报表API.md) - 包含各类统计数据查询接口。 diff --git a/docs/开发文档/API/企业信息管理API.md b/docs/开发文档/API/企业信息管理API.md index 671d93f6..f4fad7f7 100644 --- a/docs/开发文档/API/企业信息管理API.md +++ b/docs/开发文档/API/企业信息管理API.md @@ -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`,列表分页用 `TableDataInfo` | ### 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\ | **新增** 岗位列表(用于招聘简章) | +| 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 条,数据如下:
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 | 该企业下存在岗位,不能删除 | 删除有关联岗位的企业时 | \ No newline at end of file +### 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导入功能 | diff --git a/web/src/api/zsjy/enterprise.ts b/web/src/api/zsjy/enterprise.ts index fd955093..758db430 100644 --- a/web/src/api/zsjy/enterprise.ts +++ b/web/src/api/zsjy/enterprise.ts @@ -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 { @@ -19,7 +19,6 @@ export interface TableDataInfo { /** * 查询企业信息列表 * @param query 查询参数 - * @returns Promise> */ export function listEnterprise(query?: EnterpriseQuery): AxiosPromise> { return request({ @@ -32,9 +31,8 @@ export function listEnterprise(query?: EnterpriseQuery): AxiosPromise */ -export function getEnterprise(id: string | number): AxiosPromise { +export function getEnterprise(id: string | number): AxiosPromise { return request({ url: `/zsjy/enterprise/${id}`, method: 'get' @@ -42,9 +40,8 @@ export function getEnterprise(id: string | number): AxiosPromise { } /** - * 新增企业信息 + * 新增企业信息(含岗位) * @param data 企业信息 - * @returns Promise */ export function addEnterprise(data: EnterpriseForm): AxiosPromise { return request({ @@ -55,9 +52,8 @@ export function addEnterprise(data: EnterpriseForm): AxiosPromise { } /** - * 修改企业信息 + * 修改企业信息(含岗位) * @param data 企业信息 - * @returns Promise */ export function updateEnterprise(data: EnterpriseForm): AxiosPromise { return request({ @@ -70,7 +66,6 @@ export function updateEnterprise(data: EnterpriseForm): AxiosPromise { /** * 删除企业信息 * @param id 企业主键 - * @returns Promise */ export function delEnterprise(id: string | number | Array): AxiosPromise { return request({ @@ -82,7 +77,6 @@ export function delEnterprise(id: string | number | Array): Axi /** * 导出企业信息 * @param query 查询参数 - * @returns Promise */ export function exportEnterprise(query: EnterpriseQuery): AxiosPromise { return request({ @@ -92,3 +86,100 @@ export function exportEnterprise(query: EnterpriseQuery): AxiosPromise { responseType: 'blob' }); } + +/** + * 获取企业下拉选项 + */ +export function listEnterpriseOptions(): AxiosPromise { + return request({ + url: '/zsjy/enterprise/options', + method: 'get' + }); +} + +/** + * 下载导入模板 + */ +export function getImportTemplate(): AxiosPromise { + return request({ + url: '/zsjy/enterprise/importTemplate', + method: 'get', + responseType: 'blob' + }); +} + +/** + * 导入企业信息 + * @param file Excel文件 + * @param updateSupport 是否支持更新(同名覆盖) + */ +export function importEnterprise(file: File, updateSupport: boolean = false): AxiosPromise { + 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 { + return request({ + url: `/zsjy/enterprise/brochure/${enterpriseId}`, + method: 'post' + }); +} + +/** + * 批量生成招聘简章PDF + * @param enterpriseIds 企业ID列表(逗号分隔) + */ +export function generateBrochureBatch(enterpriseIds: string): AxiosPromise { + return request({ + url: `/zsjy/enterprise/brochure/batch/${enterpriseIds}`, + method: 'post' + }); +} + +/** + * 获取最新简章信息 + * @param enterpriseId 企业ID + */ +export function getBrochure(enterpriseId: number | string): AxiosPromise { + return request({ + url: `/zsjy/enterprise/brochure/${enterpriseId}`, + method: 'get' + }); +} + +/** + * 下载招聘简章PDF(通过企业ID) + * @param enterpriseId 企业ID + */ +export function downloadBrochure(enterpriseId: number | string): AxiosPromise { + return request({ + url: `/zsjy/enterprise/brochure/download/${enterpriseId}`, + method: 'get', + responseType: 'blob' + }); +} + +/** + * 下载招聘简章PDF(通过简章记录ID) + * @param id 简章记录ID + */ +export function downloadBrochureById(id: number | string): AxiosPromise { + return request({ + url: `/zsjy/enterprise/brochure/downloadById/${id}`, + method: 'get', + responseType: 'blob' + }); +} diff --git a/web/src/api/zsjy/types.ts b/web/src/api/zsjy/types.ts index 3ffec532..89e24167 100644 --- a/web/src/api/zsjy/types.ts +++ b/web/src/api/zsjy/types.ts @@ -1,396 +1,12 @@ /** - * 招生就业管理模块 - 类型定义 + * 招聘岗位 VO */ - -/** - * 企业信息 VO - */ -export interface EnterpriseVO { - /** - * 企业ID - */ - id?: string | number; - - /** - * 企业名称 - */ - enterpriseName?: string; - - /** - * 所属行业 - */ - industry?: string; - - /** - * 行业名称 - */ - industryName?: string; - - /** - * 主营业务 - */ - mainBusiness?: string; - - /** - * 企业规模 - */ - enterpriseScale?: string; - - /** - * 企业规模名称 - */ - enterpriseScaleName?: string; - - /** - * 企业联系人姓名 - */ - contactName?: string; - - /** - * 联系电话 - */ - contactPhone?: string; - - /** - * 企业邮箱 - */ - email?: string; - - /** - * 企业地址 - */ - address?: string; - - /** - * 状态(0正常 1停用) - */ - status?: string; - - /** - * 备注 - */ - remark?: string; - - /** - * 删除标志(0代表存在 1代表删除) - */ - deleted?: number; - - /** - * 租户编号 - */ - tenantId?: string; - - /** - * 创建部门 - */ - createDept?: number; - - /** - * 创建时间 - */ - createTime: string; - - /** - * 创建者 - */ - createBy?: number; - - /** - * 更新时间 - */ - updateTime?: string; - - /** - * 更新者 - */ - updateBy?: number; -} - -/** - * 企业信息表单对象 - */ -export interface EnterpriseForm extends BaseEntity { - /** - * 企业ID - */ - id?: string | number; - - /** - * 企业名称 - */ - enterpriseName: string; - - /** - * 所属行业 - */ - industry: string; - - /** - * 主营业务 - */ - mainBusiness: string; - - /** - * 企业规模 - */ - enterpriseScale: string; - - /** - * 企业联系人姓名 - */ - contactName: string; - - /** - * 联系电话 - */ - contactPhone: string; - - /** - * 企业邮箱 - */ - email: string; - - /** - * 企业地址 - */ - address: string; - - /** - * 状态(0正常 1停用) - */ - status: string; - - /** - * 备注 - */ - remark?: string; -} - -/** - * 企业信息查询条件 - */ -export interface EnterpriseQuery extends PageQuery { - /** - * 企业名称 - */ - enterpriseName?: string; - - /** - * 所属行业 - */ - industry?: string; - - /** - * 主营业务 - */ - mainBusiness?: string; - - /** - * 企业规模 - */ - enterpriseScale?: string; - - /** - * 企业联系人姓名 - */ - contactName?: string; - - /** - * 联系电话 - */ - contactPhone?: string; - - /** - * 企业邮箱 - */ - email?: string; - - /** - * 企业地址 - */ - address?: string; - - /** - * 状态(0正常 1停用) - */ - status?: string; -} - -/** - * 招聘岗位VO对象 - */ -export interface JobPositionVO { - /** - * 岗位ID - */ - id: number | string; - - /** - * 所属企业ID - */ - enterpriseId: number | string; - - /** - * 企业名称(关联字段) - */ - enterpriseName?: string; - - /** - * 岗位名称 - */ - positionName: string; - - /** - * 工作地址 - */ - workAddress: string; - - /** - * 工作时间 - */ - workTime: string; - - /** - * 薪资 - */ - salary: string; - - /** - * 所需技能 - */ - requiredSkills?: string; - - /** - * 有效期开始日期 - */ - startDate: string; - - /** - * 有效期结束日期 - */ - endDate: string; - - /** - * 是否永久有效(0否 1是) - */ - isPermanent: number; - - /** - * 状态(0正常 1停用) - */ - status: string; - - /** - * 备注 - */ - remark?: string; - - /** - * 删除标志(0代表存在 1代表删除) - */ - deleted?: number; - - /** - * 租户编号 - */ - tenantId?: string; - - /** - * 创建部门 - */ - createDept?: number; - - /** - * 创建时间 - */ - createTime: string; - - /** - * 创建者 - */ - createBy?: number; - - /** - * 更新时间 - */ - updateTime?: string; - - /** - * 更新者 - */ - updateBy?: number; -} - -/** - * 招聘岗位Form对象 - */ -export interface JobPositionForm extends BaseEntity { +export interface EnterpriseJobVO { /** * 岗位ID */ id?: number | string; - /** - * 所属企业ID - */ - enterpriseId: number | string; - - /** - * 岗位名称 - */ - positionName: string; - - /** - * 工作地址 - */ - workAddress: string; - - /** - * 工作时间 - */ - workTime: string; - - /** - * 薪资 - */ - salary: string; - - /** - * 所需技能 - */ - requiredSkills?: string; - - /** - * 有效期开始日期 - */ - startDate: string; - - /** - * 有效期结束日期 - */ - endDate: string; - - /** - * 是否永久有效(0否 1是) - */ - isPermanent: number; - - /** - * 状态(0正常 1停用) - */ - status: string; - - /** - * 备注 - */ - remark?: string; -} - -/** - * 招聘岗位Query对象 - */ -export interface JobPositionQuery extends PageQuery { /** * 所属企业ID */ @@ -402,92 +18,29 @@ export interface JobPositionQuery extends PageQuery { positionName?: string; /** - * 工作地址 + * 岗位要求 */ - workAddress?: string; + requirements?: string; + + /** + * 待遇 + */ + benefits?: string; /** * 状态(0正常 1停用) */ status?: string; - /** - * 是否永久有效 - */ - isPermanent?: number; - - /** - * 创建时间范围 - */ - params?: { - beginCreateTime?: string; - endCreateTime?: string; - }; -} - -/** - * 招聘海报VO对象 - */ -export interface RecruitmentPosterVO { - /** - * 海报ID - */ - id: number | string; - - /** - * 企业ID - */ - enterpriseId: number | string; - - /** - * 企业名称(关联字段) - */ - enterpriseName?: string; - - /** - * 岗位ID(为空表示企业整体海报) - */ - positionId?: number | string; - - /** - * 岗位名称(关联字段) - */ - positionName?: string; - - /** - * 海报文件路径 - */ - posterPath: string; - - /** - * 海报类型(1企业海报 2岗位海报) - */ - posterType: string; - - /** - * 删除标志(0代表存在 1代表删除) - */ - deleted?: number; - - /** - * 租户编号 - */ - tenantId?: string; - - /** - * 创建部门 - */ - createDept?: number; - /** * 创建时间 */ - createTime: string; + createTime?: string; /** * 创建者 */ - createBy?: number; + createBy?: number | string; /** * 更新时间 @@ -497,1061 +50,57 @@ export interface RecruitmentPosterVO { /** * 更新者 */ - updateBy?: number; + updateBy?: number | string; } /** - * 招聘海报Form对象 + * 招聘岗位表单对象 */ -export interface RecruitmentPosterForm extends BaseEntity { +export interface EnterpriseJobForm { /** - * 海报ID + * 岗位ID */ id?: number | string; /** - * 企业ID - */ - enterpriseId: number | string; - - /** - * 岗位ID(为空表示企业整体海报) - */ - positionId?: number | string; - - /** - * 海报文件路径 - */ - posterPath: string; - - /** - * 海报类型(1企业海报 2岗位海报) - */ - posterType: string; -} - -/** - * 招聘海报Query对象 - */ -export interface RecruitmentPosterQuery extends PageQuery { - /** - * 企业ID + * 所属企业ID */ enterpriseId?: number | string; - /** - * 岗位ID - */ - positionId?: number | string; - - /** - * 海报类型(1企业海报 2岗位海报) - */ - posterType?: string; -} - -/** - * 分页响应数据 - */ -export interface TableDataInfo { - total: number; - rows: T[]; - code?: number; - msg?: string; -} - -/** - * 毕业生信息VO - */ -export interface GraduateInfoVO { - id?: string | number; - studentNo?: string; - name?: string; - gender?: string; - genderName?: string; - idCard?: string; - nation?: string; - politicalStatus?: string; - birthDate?: string; - enrollmentYear?: number; - graduationYear?: number; - collegeId?: string | number; - collegeName?: string; - majorId?: string | number; - majorName?: string; - classId?: string | number; - className?: string; - educationLevel?: string; - educationLevelName?: string; - trainingMode?: string; - phoneNumber?: string; - email?: string; - qqNumber?: string; - wechatNumber?: string; - province?: string; - city?: string; - district?: string; - detailAddress?: string; - fullAddress?: string; - postalCode?: string; - employmentStatus?: string; - employmentStatusName?: string; - employmentCompany?: string; - companyType?: string; - companyTypeName?: string; - jobPosition?: string; - workProvince?: string; - workCity?: string; - workLocation?: string; - salaryRange?: string; - salaryRangeName?: string; - furtherSchool?: string; - furtherMajor?: string; - skills?: string; - awards?: string; - jobIntention?: string; - auditStatus?: string; - auditStatusName?: string; - auditTime?: string; - auditBy?: string | number; - auditByName?: string; - auditRemark?: string; - status?: string; - remark?: string; - createBy?: string | number; - createTime?: string; - updateBy?: string | number; - updateTime?: string; -} - -/** - * 毕业生信息表单 - */ -export interface GraduateInfoForm { - id?: string | number; - studentNo?: string; - name?: string; - gender?: string; - idCard?: string; - nation?: string; - politicalStatus?: string; - birthDate?: string; - enrollmentYear?: number; - graduationYear?: number; - collegeId?: string | number; - collegeName?: string; - majorId?: string | number; - majorName?: string; - classId?: string | number; - className?: string; - educationLevel?: string; - trainingMode?: string; - phoneNumber?: string; - email?: string; - qqNumber?: string; - wechatNumber?: string; - province?: string; - city?: string; - district?: string; - detailAddress?: string; - fullAddress?: string; - postalCode?: string; - employmentStatus?: string; - employmentCompany?: string; - companyType?: string; - jobPosition?: string; - workProvince?: string; - workCity?: string; - workLocation?: string; - salaryRange?: string; - furtherSchool?: string; - furtherMajor?: string; - skills?: string; - awards?: string; - jobIntention?: string; - auditStatus?: string; - auditTime?: string; - auditBy?: string | number; - auditRemark?: string; - status?: string; - remark?: string; -} - -/** - * 毕业生信息查询 - */ -export interface GraduateInfoQuery extends PageQuery { - studentNo?: string; - name?: string; - gender?: string; - idCard?: string; - nation?: string; - politicalStatus?: string; - birthDate?: string; - enrollmentYear?: number; - graduationYear?: number; - collegeId?: string | number; - collegeName?: string; - majorId?: string | number; - majorName?: string; - classId?: string | number; - className?: string; - educationLevel?: string; - trainingMode?: string; - phoneNumber?: string; - email?: string; - employmentStatus?: string; - employmentCompany?: string; - companyType?: string; - jobPosition?: string; - workProvince?: string; - workCity?: string; - salaryRange?: string; - furtherSchool?: string; - furtherMajor?: string; - auditStatus?: string; - auditBy?: string | number; - status?: string; - params?: any; -} - -/** - * 毕业生信息统计VO - */ -export interface GraduateStatisticsVO { - name?: string; - value?: number; - total?: number; - percentage?: number; - employmentCount?: number; - employmentRate?: number; - furtherStudyCount?: number; - furtherStudyRate?: number; - unemploymentCount?: number; - type?: string; - year?: number; - collegeId?: string | number; - collegeName?: string; - majorId?: string | number; - majorName?: string; - employmentStatus?: string; - employmentStatusName?: string; - companyType?: string; - companyTypeName?: string; - province?: string; - city?: string; - salaryRange?: string; - salaryRangeName?: string; -} - -/** - * 实习信息 VO - */ -export interface InternshipInfoVO { - /** - * 学生ID - */ - studentId?: string | number; - - /** - * 学号 - */ - studentNo?: string; - - /** - * 学生姓名 - */ - studentName?: string; - - /** - * 性别 - */ - gender?: string; - - /** - * 手机号 - */ - phone?: string; - - /** - * 学院ID - */ - collegeId?: string | number; - - /** - * 学院名称 - */ - collegeName?: string; - - /** - * 专业ID - */ - majorId?: string | number; - - /** - * 专业名称 - */ - majorName?: string; - - /** - * 班级ID - */ - classId?: string | number; - - /** - * 班级名称 - */ - className?: string; - - /** - * 班主任姓名 - */ - classTeacherName?: string; - - /** - * 实习状态 (0=未实习, 1=实习中, 2=实习完成) - */ - internshipStatus?: string; - - /** - * 实习状态名称 - */ - internshipStatusName?: string; - - /** - * 实习公司 - */ - companyName?: string; - - /** - * 实习岗位 - */ - internshipPosition?: string; - - /** - * 实习开始时间 - */ - internshipStartDate?: string; - - /** - * 实习结束时间 - */ - internshipEndDate?: string; - - /** - * 实习地点 - */ - companyAddress?: string; - - /** - * 实习津贴 - */ - internshipAllowance?: number; - - /** - * 单位联系人 - */ - contactPersonName?: string; - - /** - * 联系人电话 - */ - contactPersonPhone?: string; - - /** - * 协议状态 - */ - agreementStatus?: string; - - /** - * 备注信息 - */ - remark?: string; - - /** - * 创建时间 - */ - createTime?: string; - - /** - * 创建者 - */ - createBy?: number; - - /** - * 更新时间 - */ - updateTime?: string; - - /** - * 更新者 - */ - updateBy?: number; -} - -/** - * 实习信息新增 BO - */ -export interface InternshipInfoAddBO { - /** - * 学生ID - */ - studentId: string | number; - - /** - * 学生姓名(前端显示用) - */ - studentName?: string; - - /** - * 学生学号(前端显示用) - */ - studentNo?: string; - - /** - * 实习状态(1进行中 2已完成 3中断) - */ - internshipStatus: string; - - /** - * 实习单位名称 - */ - companyName: string; - - /** - * 实习单位地址 - */ - companyAddress: string; - - /** - * 单位联系人 - */ - companyContact?: string; - - /** - * 联系人电话 - */ - companyPhone?: string; - - /** - * 行业类型 - */ - companyIndustry?: string; - - /** - * 单位规模 - */ - companyScale?: string; - - /** - * 实习岗位 - */ - internshipPosition: string; - - /** - * 实习薪资 - */ - internshipAllowance?: number; - - /** - * 实习开始时间 - */ - internshipStartDate: string; - - /** - * 实习结束时间 - */ - internshipEndDate: string; - - /** - * 联系人姓名 - */ - contactPersonName: string; - - /** - * 联系人电话 - */ - contactPersonPhone: string; - - /** - * 岗位职责 - */ - positionDuties?: string; - - /** - * 工作时长 - */ - workHours?: string; - - /** - * 住宿地址 - */ - accommodationAddress?: string; - - /** - * 住宿类型 - */ - accommodationType?: string; - - /** - * 住宿费用 - */ - accommodationCost?: number; - - /** - * 实习协议状态(1已签署 2未签署) - */ - agreementStatus: string; - - /** - * 备注 - */ - remark?: string; -} - -/** - * 实习信息编辑 BO - */ -export interface InternshipInfoEditBO { - /** - * 实习信息ID - */ - internshipId: string | number; - - /** - * 学生ID - */ - studentId: string | number; - - /** - * 学生姓名(前端显示用) - */ - studentName?: string; - - /** - * 学生学号(前端显示用) - */ - studentNo?: string; - - /** - * 实习状态(1进行中 2已完成 3中断) - */ - internshipStatus: string; - - /** - * 实习单位名称 - */ - companyName: string; - - /** - * 实习单位地址 - */ - companyAddress: string; - - /** - * 单位联系人 - */ - companyContact?: string; - - /** - * 联系人电话 - */ - companyPhone?: string; - - /** - * 行业类型 - */ - companyIndustry?: string; - - /** - * 单位规模 - */ - companyScale?: string; - - /** - * 实习岗位 - */ - internshipPosition: string; - - /** - * 实习薪资 - */ - internshipAllowance?: number; - - /** - * 实习开始时间 - */ - internshipStartDate: string; - - /** - * 实习结束时间 - */ - internshipEndDate: string; - - /** - * 联系人姓名 - */ - contactPersonName: string; - - /** - * 联系人电话 - */ - contactPersonPhone: string; - - /** - * 岗位职责 - */ - positionDuties?: string; - - /** - * 工作时长 - */ - workHours?: string; - - /** - * 住宿地址 - */ - accommodationAddress?: string; - - /** - * 住宿类型 - */ - accommodationType?: string; - - /** - * 住宿费用 - */ - accommodationCost?: number; - - /** - * 实习协议状态(1已签署 2未签署) - */ - agreementStatus: string; - - /** - * 实际结束日期 - */ - actualEndDate?: string; - - /** - * 备注 - */ - remark?: string; -} - -/** - * 实习信息变更 BO - */ -export interface InternshipInfoChangeBO { - /** - * 原实习信息ID(要变更的实习记录ID) - */ - internshipId: string | number; - - /** - * 学生ID - */ - studentId: string | number; - - /** - * 变更原因(必填) - */ - changeReason: string; - - /** - * 实习状态(1进行中 2已完成 3中断) - */ - internshipStatus: string; - - /** - * 实习单位名称 - */ - companyName: string; - - /** - * 实习单位地址 - */ - companyAddress: string; - - /** - * 单位联系人 - */ - companyContact: string; - - /** - * 联系人电话 - */ - companyPhone: string; - - /** - * 行业类型 - */ - companyIndustry?: string; - - /** - * 单位规模 - */ - companyScale?: string; - /** * 岗位名称 */ positionName: string; /** - * 岗位职责 + * 岗位要求 */ - positionDuties?: string; + requirements?: string; /** - * 实习津贴 + * 待遇 */ - internshipAllowance?: number; + benefits?: string; /** - * 工作时长 - */ - workHours?: string; - - /** - * 住宿地址 - */ - accommodationAddress?: string; - - /** - * 住宿类型 - */ - accommodationType?: string; - - /** - * 住宿费用 - */ - accommodationCost?: number; - - /** - * 实习开始日期 - */ - startDate: string; - - /** - * 实习结束日期 - */ - endDate: string; - - /** - * 备注信息 - */ - remark?: string; -} - -/** - * 实习信息查询 BO - */ -export interface InternshipInfoQuery { - /** - * 页码 - */ - pageNum?: number; - - /** - * 页面大小 - */ - pageSize?: number; - - /** - * 学生姓名 - */ - studentName?: string; - - /** - * 学号 - */ - studentNo?: string; - - /** - * 学院ID - */ - collegeId?: string | number; - - /** - * 专业ID - */ - majorId?: string | number; - - /** - * 班级ID - */ - classId?: string | number; - - /** - * 实习状态 (0=未实习, 1=实习中, 2=实习完成) - */ - internshipStatus?: string; - - /** - * 实习公司 - */ - companyName?: string; - - /** - * 实习开始日期-开始 - */ - internshipStartDateBegin?: string; - - /** - * 实习开始日期-结束 - */ - internshipStartDateEnd?: string; - - /** - * 班主任ID(用于数据权限控制) - */ - classTeacherId?: string | number; - - /** - * 辅导员ID(用于数据权限控制) - */ - counselorId?: string | number; -} - -/** - * 班级信息 VO(用于下拉选择) - */ -export interface ClassInfoVO { - /** - * 班级ID - */ - classId?: string | number; - - /** - * 班级名称 - */ - className?: string; - - /** - * 学院ID - */ - collegeId?: string | number; - - /** - * 学院名称 - */ - collegeName?: string; - - /** - * 专业ID - */ - majorId?: string | number; - - /** - * 专业名称 - */ - majorName?: string; - - /** - * 班主任姓名 - */ - classMasterName?: string; -} - -/** - * 实习统计信息 VO - */ -export interface InternshipStatisticsVO { - /** - * 总学生数 - */ - totalCount?: number; - - /** - * 未实习学生数 - */ - noInternshipCount?: number; - - /** - * 实习学生数 - */ - internshipCount?: number; - - /** - * 实习率(百分比字符串,如 "85.67%") - */ - internshipRate?: string; -} - -/** - * 班主任走访记录 VO - */ -export interface InternshipVisitVO { - /** - * 走访记录ID - */ - visitId?: string | number; - - /** - * 实习记录ID - */ - internshipId?: string | number; - - /** - * 学生ID - */ - studentId?: string | number; - - /** - * 学生姓名 - */ - studentName?: string; - - /** - * 学号 - */ - studentNumber?: string; - - /** - * 班级名称 - */ - className?: string; - - /** - * 专业名称 - */ - majorName?: string; - - /** - * 学院名称 - */ - collegeName?: string; - - /** - * 企业名称 - */ - companyName?: string; - - /** - * 企业地址 - */ - companyAddress?: string; - - /** - * 走访日期 - */ - visitDate?: string; - - /** - * 走访人员 - */ - visitorName?: string; - - /** - * 走访人员职务 - */ - visitorPosition?: string; - - /** - * 走访目的 - */ - visitPurpose?: string; - - /** - * 企业情况 - */ - companySituation?: string; - - /** - * 学生表现 - */ - studentPerformance?: string; - - /** - * 发现问题 - */ - problemsFound?: string; - - /** - * 改进建议 - */ - suggestions?: string; - - /** - * 走访总结 - */ - visitSummary?: string; - - /** - * 现场照片(JSON格式) - */ - visitImages?: string; - - /** - * 走访文件附件(JSON格式) - */ - visitFiles?: string; - - /** - * 状态(0-草稿 1-已提交 2-已审核) + * 状态(0正常 1停用) */ status?: string; - - /** - * 备注 - */ - remark?: string; - - /** - * 创建时间 - */ - createTime?: string; - - /** - * 创建者 - */ - createBy?: number; - - /** - * 更新时间 - */ - updateTime?: string; - - /** - * 更新者 - */ - updateBy?: number; } /** - * 班主任走访记录查询参数 + * 招聘岗位查询对象 */ -export interface InternshipVisitQuery extends PageQuery { +export interface EnterpriseJobQuery { /** - * 学生姓名 + * 所属企业ID */ - studentName?: string; + enterpriseId?: number | string; /** - * 学号 + * 岗位名称 */ - studentNumber?: string; - - /** - * 企业名称 - */ - companyName?: string; - - /** - * 走访人员 - */ - visitorName?: string; - - /** - * 走访日期-开始 - */ - visitDateBegin?: string; - - /** - * 走访日期-结束 - */ - visitDateEnd?: string; + positionName?: string; /** * 状态 @@ -1560,296 +109,56 @@ export interface InternshipVisitQuery extends PageQuery { } /** - * 班主任走访记录新增参数 + * 简章生成记录 VO */ -export interface InternshipVisitAddBO { +export interface EnterpriseBrochureVO { /** - * 实习记录ID + * 记录ID */ - internshipId?: string | number; + id?: number | string; /** - * 学生ID + * 企业ID */ - studentId?: string | number; + enterpriseId?: number | string; /** * 企业名称 */ - companyName?: string; + enterpriseName?: string; /** - * 企业地址 + * PDF存储路径 */ - companyAddress?: string; + brochurePath?: string; /** - * 走访日期 + * 文件名称 */ - visitDate?: string; + fileName?: string; /** - * 走访人员 + * 文件大小 */ - visitorName?: string; + fileSize?: number; /** - * 走访人员职务 + * 生成人ID */ - visitorPosition?: string; + generateBy?: number | string; /** - * 走访目的 + * 生成人姓名 */ - visitPurpose?: string; + generateByName?: string; /** - * 企业情况 + * 生成时间 */ - companySituation?: string; - - /** - * 学生表现 - */ - studentPerformance?: string; - - /** - * 发现问题 - */ - problemsFound?: string; - - /** - * 改进建议 - */ - suggestions?: string; - - /** - * 走访总结 - */ - visitSummary?: string; - - /** - * 现场照片(JSON格式) - */ - visitImages?: string; - - /** - * 走访文件附件(JSON格式) - */ - visitFiles?: string; - - /** - * 状态 - */ - status?: string; - - /** - * 备注 - */ - remark?: string; -} - -/** - * 班主任走访记录编辑参数 - */ -export interface InternshipVisitEditBO extends InternshipVisitAddBO { - /** - * 走访记录ID - */ - visitId?: string | number; -} - -/** - * 实习月度总结 VO - */ -export interface InternshipMonthlyVO { - /** - * 月度总结ID - */ - id?: string | number; - - /** - * 学生ID - */ - studentId?: string | number; - - /** - * 学生姓名 - */ - studentName?: string; - - /** - * 学号 - */ - studentNumber?: string; - - /** - * 班级名称 - */ - className?: string; - - /** - * 专业名称 - */ - majorName?: string; - - /** - * 学院名称 - */ - collegeName?: string; - - /** - * 总结月份 - */ - summaryMonth?: string; - - /** - * 工作内容 - */ - workContent?: string; - - /** - * 学习收获 - */ - learningGains?: string; - - /** - * 问题与困难 - */ - problemsEncountered?: string; - - /** - * 改进计划 - */ - improvementPlan?: string; - - /** - * 下月计划 - */ - nextMonthPlan?: string; - - /** - * 专业技能评分 (1-10分) - */ - professionalSkills?: number; - - /** - * 沟通能力评分 (1-10分) - */ - communicationSkills?: number; - - /** - * 团队协作评分 (1-10分) - */ - teamworkAbility?: number; - - /** - * 学习能力评分 (1-10分) - */ - learningAbility?: number; - - /** - * 工作态度评分 (1-10分) - */ - workAttitude?: number; - - /** - * 字数统计 - */ - wordCount?: number; - - /** - * 审核状态 (0-待审核 1-已通过 2-已拒绝 3-草稿) - */ - reviewStatus?: number; - - /** - * 审核人ID - */ - reviewerId?: string | number; - - /** - * 审核人姓名 - */ - reviewerName?: string; - - /** - * 审核评分 (0-100分) - */ - reviewScore?: number; - - /** - * 审核意见 - */ - reviewComment?: string; - - /** - * 改进建议 - */ - reviewSuggestions?: string; - - /** - * 审核时间 - */ - reviewTime?: string; - - /** - * 是否逾期 - */ - isOverdue?: boolean; + generateTime?: string; /** * 创建时间 */ createTime?: string; - - /** - * 创建者 - */ - createBy?: number; - - /** - * 更新时间 - */ - updateTime?: string; - - /** - * 更新者 - */ - updateBy?: number; -} - -/** - * 实习月度总结审核统计 VO - */ -export interface InternshipMonthlyReviewStatisticsVO { - /** - * 总记录数 - */ - totalCount?: number; - - /** - * 待审核数量 - */ - pendingCount?: number; - - /** - * 已通过数量 - */ - approvedCount?: number; - - /** - * 已拒绝数量 - */ - rejectedCount?: number; - - /** - * 平均评分 - */ - averageScore?: number; - - /** - * 完成率(百分比) - */ - completionRate?: number; } diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index 7ae6f71b..f1bc786b 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -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) => { diff --git a/web/src/views/zsjy/enterprise/index.vue b/web/src/views/zsjy/enterprise/index.vue index 547befbd..10dda6b6 100644 --- a/web/src/views/zsjy/enterprise/index.vue +++ b/web/src/views/zsjy/enterprise/index.vue @@ -63,6 +63,12 @@ 导出 + + 导入 + + + 批量生成简章 + @@ -83,15 +89,18 @@ - - + + + - - + +