diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizHospitalController.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizHospitalController.java
new file mode 100644
index 0000000..cba8bab
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/controller/BizHospitalController.java
@@ -0,0 +1,119 @@
+package com.ruoyi.business.controller;
+
+import java.util.List;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.business.domain.BizHospital;
+import com.ruoyi.business.domain.dto.ImportResult;
+import com.ruoyi.business.domain.vo.BizHospitalExportVo;
+import com.ruoyi.business.domain.vo.BizHospitalImportVo;
+import com.ruoyi.business.service.IBizHospitalService;
+
+/**
+ * 医院字典 Controller (admin 资料库管理 / 医院管理)
+ *
+ *
CRUD + 导入/导出走 admin 路由 (前端 AdminLayout 角色控制);
+ * 远程搜索 /search 为匿名公开 (医生注册下拉).
+ * 注: 不加 @PreAuthorize — 与 BizExpert/BizOrg 等业务 controller 一致 (业务角色不走 RuoYi RBAC).
+ *
+ * @author guoju
+ */
+@RestController
+@RequestMapping("/business/hospital")
+public class BizHospitalController extends BaseController {
+
+ @Autowired
+ private IBizHospitalService bizHospitalService;
+
+ /** 分页列表 (admin 后台, 支持 hospital/province/city/hospitalCode/militaryHospital/type 筛选) */
+ @GetMapping("/list")
+ public TableDataInfo list(BizHospital entity) {
+ startPage();
+ List rows = bizHospitalService.selectList(entity);
+ return getDataTable(rows);
+ }
+
+ /** 详情 */
+ @GetMapping("/{hospitalId}")
+ public AjaxResult getInfo(@PathVariable("hospitalId") Long hospitalId) {
+ return success(bizHospitalService.getById(hospitalId));
+ }
+
+ /**
+ * 远程搜索 (匿名公开, 医生注册下拉"边输入边查询")
+ * GET /business/hospital/search?keyword=xxx → [{hospitalId, province, city, hospital, ...}] LIMIT 30
+ */
+ @GetMapping("/search")
+ public AjaxResult search(@RequestParam(value = "keyword", required = false) String keyword) {
+ return success(bizHospitalService.searchByKeyword(keyword));
+ }
+
+ /** 删除前引用统计: 返回医院名被业务表 work_unit 引用的条数 */
+ @GetMapping("/countUsage")
+ public AjaxResult countUsage(@RequestParam("hospital") String hospital) {
+ return success(bizHospitalService.countUsageByHospital(hospital));
+ }
+
+ @Log(title = "医院管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody BizHospital entity) {
+ return toAjax(bizHospitalService.insert(entity));
+ }
+
+ @Log(title = "医院管理", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody BizHospital entity) {
+ return toAjax(bizHospitalService.updateByPrimaryKey(entity));
+ }
+
+ @Log(title = "医院管理", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{hospitalIds}")
+ public AjaxResult remove(@PathVariable Long[] hospitalIds) {
+ return toAjax(bizHospitalService.deleteByPrimaryKeys(hospitalIds));
+ }
+
+ /** 导出 (中文列头, 跟随当前筛选条件) */
+ @Log(title = "医院管理", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, BizHospital entity) {
+ List list = bizHospitalService.selectList(entity);
+ List exportList = new java.util.ArrayList<>(list.size());
+ for (BizHospital h : list) {
+ BizHospitalExportVo v = new BizHospitalExportVo();
+ v.setHospitalId(h.getHospitalId());
+ v.setProvince(h.getProvince());
+ v.setCity(h.getCity());
+ v.setHospital(h.getHospital());
+ v.setOriginalHospital(h.getOriginalHospital());
+ v.setHospitalCode(h.getHospitalCode());
+ v.setMilitaryHospital(h.getMilitaryHospital());
+ v.setType(h.getType());
+ exportList.add(v);
+ }
+ ExcelUtil util = new ExcelUtil<>(BizHospitalExportVo.class);
+ util.exportExcel(response, exportList, "医院数据");
+ }
+
+ /** 下载导入模板 */
+ @GetMapping("/importTemplate")
+ public void importTemplate(HttpServletResponse response) {
+ ExcelUtil util = new ExcelUtil<>(BizHospitalImportVo.class);
+ util.importTemplateExcel(response, "医院数据");
+ }
+
+ /** 批量导入 (Excel → biz_hospital, 去重, 返回 ImportResult) */
+ @Log(title = "医院管理", businessType = BusinessType.IMPORT)
+ @PostMapping("/importData")
+ public AjaxResult importData(MultipartFile file) throws Exception {
+ ImportResult result = bizHospitalService.importHospitals(file);
+ return success(result);
+ }
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizHospital.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizHospital.java
new file mode 100644
index 0000000..10f4c0f
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/BizHospital.java
@@ -0,0 +1,84 @@
+package com.ruoyi.business.domain;
+
+import com.ruoyi.common.annotation.Excel;
+
+/**
+ * 医院字典 (biz_hospital)
+ *
+ * admin 用户在"资料库管理 / 医院管理"下维护; 医生注册时通过 /business/hospital/search 远程搜索选择.
+ *
+ *
字段说明:
+ *
+ * - hospitalId — 雪花 ID (Long), 由 SnowflakeId.injectIfEmpty 生成; 53-bit, JS Number 安全
+ * - province/city — 省/市 (varchar 100)
+ * - hospital — 医院名称 (varchar 1000, 主显示名)
+ * - originalHospital — 原名称 (varchar 100)
+ * - hospitalCode — 医院 CODE (varchar 100, 如 H3301006)
+ * - militaryHospital — 是否军医院 (varchar 100, 现存数据为 '0'/'1')
+ * - type — 类型 (varchar 100, 现存数据为 '0'/'1')
+ *
+ *
+ * 说明: 原表 schema 中 id 为 int NOT NULL 无 auto_increment, 改 BIGINT 由应用生成雪花 ID (与项目其他表风格一致).
+ *
+ * @author guoju
+ */
+public class BizHospital {
+
+ private static final long serialVersionUID = 1L;
+
+ /** 医院 ID (雪花 ID, BIGINT) */
+ @Excel(name = "医院ID")
+ private Long hospitalId;
+
+ /** 省 */
+ @Excel(name = "省")
+ private String province;
+
+ /** 市 */
+ @Excel(name = "市")
+ private String city;
+
+ /** 医院名称 (主显示名) */
+ @Excel(name = "医院名称")
+ private String hospital;
+
+ /** 原名称 */
+ @Excel(name = "原名称")
+ private String originalHospital;
+
+ /** 医院 CODE */
+ @Excel(name = "医院CODE")
+ private String hospitalCode;
+
+ /** 是否军医院 */
+ @Excel(name = "是否军医院")
+ private String militaryHospital;
+
+ /** 类型 */
+ @Excel(name = "类型")
+ private String type;
+
+ public Long getHospitalId() { return hospitalId; }
+ public void setHospitalId(Long hospitalId) { this.hospitalId = hospitalId; }
+
+ public String getProvince() { return province; }
+ public void setProvince(String province) { this.province = province; }
+
+ public String getCity() { return city; }
+ public void setCity(String city) { this.city = city; }
+
+ public String getHospital() { return hospital; }
+ public void setHospital(String hospital) { this.hospital = hospital; }
+
+ public String getOriginalHospital() { return originalHospital; }
+ public void setOriginalHospital(String originalHospital) { this.originalHospital = originalHospital; }
+
+ public String getHospitalCode() { return hospitalCode; }
+ public void setHospitalCode(String hospitalCode) { this.hospitalCode = hospitalCode; }
+
+ public String getMilitaryHospital() { return militaryHospital; }
+ public void setMilitaryHospital(String militaryHospital) { this.militaryHospital = militaryHospital; }
+
+ public String getType() { return type; }
+ public void setType(String type) { this.type = type; }
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/vo/BizHospitalExportVo.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/vo/BizHospitalExportVo.java
new file mode 100644
index 0000000..e8ae5b3
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/vo/BizHospitalExportVo.java
@@ -0,0 +1,61 @@
+package com.ruoyi.business.domain.vo;
+
+import com.ruoyi.common.annotation.Excel;
+
+/**
+ * 医院字典导出 VO
+ *
+ *
仅用于 Excel 导出, 不参与业务逻辑.
+ *
+ * @author guoju
+ */
+public class BizHospitalExportVo {
+
+ @Excel(name = "医院ID", sort = 1)
+ private Long hospitalId;
+
+ @Excel(name = "省", sort = 2)
+ private String province;
+
+ @Excel(name = "市", sort = 3)
+ private String city;
+
+ @Excel(name = "医院名称", sort = 4)
+ private String hospital;
+
+ @Excel(name = "原名称", sort = 5)
+ private String originalHospital;
+
+ @Excel(name = "医院CODE", sort = 6)
+ private String hospitalCode;
+
+ @Excel(name = "是否军医院", sort = 7)
+ private String militaryHospital;
+
+ @Excel(name = "类型", sort = 8)
+ private String type;
+
+ public Long getHospitalId() { return hospitalId; }
+ public void setHospitalId(Long hospitalId) { this.hospitalId = hospitalId; }
+
+ public String getProvince() { return province; }
+ public void setProvince(String province) { this.province = province; }
+
+ public String getCity() { return city; }
+ public void setCity(String city) { this.city = city; }
+
+ public String getHospital() { return hospital; }
+ public void setHospital(String hospital) { this.hospital = hospital; }
+
+ public String getOriginalHospital() { return originalHospital; }
+ public void setOriginalHospital(String originalHospital) { this.originalHospital = originalHospital; }
+
+ public String getHospitalCode() { return hospitalCode; }
+ public void setHospitalCode(String hospitalCode) { this.hospitalCode = hospitalCode; }
+
+ public String getMilitaryHospital() { return militaryHospital; }
+ public void setMilitaryHospital(String militaryHospital) { this.militaryHospital = militaryHospital; }
+
+ public String getType() { return type; }
+ public void setType(String type) { this.type = type; }
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/vo/BizHospitalImportVo.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/vo/BizHospitalImportVo.java
new file mode 100644
index 0000000..c3fc79b
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/domain/vo/BizHospitalImportVo.java
@@ -0,0 +1,66 @@
+package com.ruoyi.business.domain.vo;
+
+import com.ruoyi.common.annotation.Excel;
+
+/**
+ * 医院字典导入 VO
+ *
+ *
仅用于 Excel 批量导入 / 模板下载, 不参与业务逻辑.
+ * 字段顺序与 Excel 列一致; hospitalId 不在导入范围 (由 SnowflakeId 生成).
+ *
+ *
必填: hospital (医院名称).
+ * 其余字段允许空.
+ *
+ * @author guoju
+ */
+public class BizHospitalImportVo {
+
+ /** 省 */
+ @Excel(name = "省", sort = 1)
+ private String province;
+
+ /** 市 */
+ @Excel(name = "市", sort = 2)
+ private String city;
+
+ /** 医院名称 (必填) */
+ @Excel(name = "医院名称", sort = 3)
+ private String hospital;
+
+ /** 原名称 */
+ @Excel(name = "原名称", sort = 4)
+ private String originalHospital;
+
+ /** 医院 CODE */
+ @Excel(name = "医院CODE", sort = 5)
+ private String hospitalCode;
+
+ /** 是否军医院 (0=否, 1=是) */
+ @Excel(name = "是否军医院", sort = 6)
+ private String militaryHospital;
+
+ /** 类型 */
+ @Excel(name = "类型", sort = 7)
+ private String type;
+
+ public String getProvince() { return province; }
+ public void setProvince(String province) { this.province = province; }
+
+ public String getCity() { return city; }
+ public void setCity(String city) { this.city = city; }
+
+ public String getHospital() { return hospital; }
+ public void setHospital(String hospital) { this.hospital = hospital; }
+
+ public String getOriginalHospital() { return originalHospital; }
+ public void setOriginalHospital(String originalHospital) { this.originalHospital = originalHospital; }
+
+ public String getHospitalCode() { return hospitalCode; }
+ public void setHospitalCode(String hospitalCode) { this.hospitalCode = hospitalCode; }
+
+ public String getMilitaryHospital() { return militaryHospital; }
+ public void setMilitaryHospital(String militaryHospital) { this.militaryHospital = militaryHospital; }
+
+ public String getType() { return type; }
+ public void setType(String type) { this.type = type; }
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/mapper/BizHospitalMapper.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/mapper/BizHospitalMapper.java
new file mode 100644
index 0000000..cf4056b
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/mapper/BizHospitalMapper.java
@@ -0,0 +1,52 @@
+package com.ruoyi.business.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.business.domain.BizHospital;
+
+/**
+ * 医院字典 Mapper (biz_hospital)
+ *
+ *
无 del_flag 列 — 删除走硬删 (admin 字典, workUnit 字符串引用, 删除不会破坏业务数据完整性).
+ *
+ * @author guoju
+ */
+public interface BizHospitalMapper {
+
+ /** 按主键查询 */
+ BizHospital selectByPrimaryKey(@Param("hospitalId") Long hospitalId);
+
+ /** 分页 + 多条件模糊筛选 (admin 后台用, 支持 hospital/province/type/militaryHospital) */
+ List selectList(BizHospital entity);
+
+ /** 新增 */
+ int insert(BizHospital entity);
+
+ /** 按主键更新 (动态 trim SET) */
+ int updateByPrimaryKey(BizHospital entity);
+
+ /** 批量硬删 */
+ int deleteByPrimaryKeys(@Param("hospitalIds") Long[] hospitalIds);
+
+ /**
+ * 远程搜索: 按关键字模糊匹配 hospital / originalHospital / hospitalCode,
+ * LIMIT 30 (医生注册下拉防爆).
+ * keyword 为空时返回空集 (前端按"边输入边查询"约定不主动出建议).
+ */
+ List searchByKeyword(@Param("keyword") String keyword);
+
+ /**
+ * 唯一性检查: 按 trim(hospital) + province + city 精确匹配 (case-insensitive).
+ * hospitalId 非空时排除自身 (改名/同名异市用); 用于新增/编辑前的"同名医院"提示.
+ * 返回匹配条数 (0 或 1).
+ */
+ int countByTrimmedHospital(BizHospital entity);
+
+ /**
+ * 统计指定医院名称被业务表 work_unit 字段引用的总条数.
+ * 用于删除前给 admin 二次确认: "该医院已被 N 条专家/参会人引用".
+ * 4 表 union: biz_expert / biz_signup_expert / biz_meeting_attendee / biz_execution_intent (有 work_unit 列)
+ * 注: biz_support_intent 也有 work_unit 列, 一起并入.
+ */
+ int countUsageByHospital(@Param("hospital") String hospital);
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/IBizHospitalService.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/IBizHospitalService.java
new file mode 100644
index 0000000..eb62c1b
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/IBizHospitalService.java
@@ -0,0 +1,41 @@
+package com.ruoyi.business.service;
+
+import java.util.List;
+import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.business.domain.BizHospital;
+import com.ruoyi.business.domain.dto.ImportResult;
+
+/**
+ * 医院字典 Service 接口 (biz_hospital)
+ *
+ * @author guoju
+ */
+public interface IBizHospitalService {
+
+ /** 按主键查询 */
+ BizHospital getById(Long hospitalId);
+
+ /** 分页 + 多条件筛选 */
+ List selectList(BizHospital entity);
+
+ /** 新增 (医院名查重 + 雪花 ID) */
+ int insert(BizHospital entity);
+
+ /** 编辑 (医院名查重) */
+ int updateByPrimaryKey(BizHospital entity);
+
+ /** 批量硬删 */
+ int deleteByPrimaryKeys(Long[] hospitalIds);
+
+ /** 远程搜索 (医生注册下拉, LIMIT 30) */
+ List searchByKeyword(String keyword);
+
+ /** 唯一性检查: 同 name + province + city 是否已存在 (排除自身) */
+ int countByTrimmedHospital(BizHospital entity);
+
+ /** 统计医院名被业务表 work_unit 引用的条数 (删除前二次确认用) */
+ int countUsageByHospital(String hospital);
+
+ /** 批量导入 (Excel → biz_hospital, 批内/DB 去重, 返回 ImportResult) */
+ ImportResult importHospitals(MultipartFile file) throws Exception;
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizHospitalServiceImpl.java b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizHospitalServiceImpl.java
new file mode 100644
index 0000000..f2aa649
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/java/com/ruoyi/business/service/impl/BizHospitalServiceImpl.java
@@ -0,0 +1,193 @@
+package com.ruoyi.business.service.impl;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.business.domain.BizHospital;
+import com.ruoyi.business.domain.dto.ImportResult;
+import com.ruoyi.business.domain.vo.BizHospitalImportVo;
+import com.ruoyi.business.mapper.BizHospitalMapper;
+import com.ruoyi.business.service.IBizHospitalService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.id.SnowflakeId;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+
+/**
+ * 医院字典 Service 实现 (biz_hospital)
+ *
+ * 要点:
+ *
+ * - 所有字符串字段入库前 trim (防首尾空白脏数据)
+ * - 医院名 + province + city 判重 (trim + case-insensitive)
+ * - 主键走雪花 ID (SnowflakeId.injectIfEmpty) — 前提: id 列已 ALTER 为 BIGINT
+ * - 删除走硬删 (字典表, workUnit 为字符串无外键, 删除不破坏业务完整性)
+ *
+ *
+ * @author guoju
+ */
+@Service
+public class BizHospitalServiceImpl implements IBizHospitalService {
+
+ @Autowired
+ private BizHospitalMapper bizHospitalMapper;
+
+ @Override
+ public BizHospital getById(Long hospitalId) {
+ return bizHospitalMapper.selectByPrimaryKey(hospitalId);
+ }
+
+ @Override
+ public List selectList(BizHospital entity) {
+ return bizHospitalMapper.selectList(entity);
+ }
+
+ @Override
+ public int insert(BizHospital entity) {
+ String hospital = trimToNull(entity.getHospital());
+ if (hospital == null) {
+ throw new ServiceException("医院名称不能为空");
+ }
+ entity.setHospital(hospital);
+ normalize(entity);
+
+ // 同名医院查重 (trim + case-insensitive + province/city 匹配)
+ BizHospital probe = new BizHospital();
+ probe.setHospital(hospital);
+ probe.setProvince(entity.getProvince());
+ probe.setCity(entity.getCity());
+ if (bizHospitalMapper.countByTrimmedHospital(probe) > 0) {
+ throw new ServiceException("医院「" + hospital + "」已存在");
+ }
+
+ SnowflakeId.injectIfEmpty(entity, "hospitalId");
+ return bizHospitalMapper.insert(entity);
+ }
+
+ @Override
+ public int updateByPrimaryKey(BizHospital entity) {
+ if (entity.getHospitalId() == null) {
+ throw new ServiceException("医院 ID 不能为空");
+ }
+ String hospital = trimToNull(entity.getHospital());
+ if (hospital == null) {
+ throw new ServiceException("医院名称不能为空");
+ }
+ entity.setHospital(hospital);
+ normalize(entity);
+
+ // 改名时同样查重 (排除自身)
+ BizHospital probe = new BizHospital();
+ probe.setHospital(hospital);
+ probe.setProvince(entity.getProvince());
+ probe.setCity(entity.getCity());
+ probe.setHospitalId(entity.getHospitalId());
+ if (bizHospitalMapper.countByTrimmedHospital(probe) > 0) {
+ throw new ServiceException("医院「" + hospital + "」已存在");
+ }
+
+ return bizHospitalMapper.updateByPrimaryKey(entity);
+ }
+
+ @Override
+ public int deleteByPrimaryKeys(Long[] hospitalIds) {
+ return bizHospitalMapper.deleteByPrimaryKeys(hospitalIds);
+ }
+
+ @Override
+ public List searchByKeyword(String keyword) {
+ return bizHospitalMapper.searchByKeyword(keyword);
+ }
+
+ @Override
+ public int countByTrimmedHospital(BizHospital entity) {
+ return bizHospitalMapper.countByTrimmedHospital(entity);
+ }
+
+ @Override
+ public int countUsageByHospital(String hospital) {
+ if (hospital == null || hospital.trim().isEmpty()) {
+ return 0;
+ }
+ return bizHospitalMapper.countUsageByHospital(hospital.trim());
+ }
+
+ /**
+ * 批量导入: Excel → biz_hospital.
+ *
+ * - ExcelUtil 反序列化
+ * - 行级校验 (医院名必填) + 批内去重 + DB 去重
+ * - 逐行 insert (雪花 ID), 失败记 ngList 不影响其它行
+ *
+ */
+ @Override
+ public ImportResult importHospitals(MultipartFile file) throws Exception {
+ ExcelUtil util = new ExcelUtil<>(BizHospitalImportVo.class);
+ List importList = util.importExcel(file.getInputStream());
+ if (importList == null || importList.isEmpty()) {
+ throw new ServiceException("导入数据不能为空");
+ }
+ ImportResult result = new ImportResult();
+ Set seenHospital = new HashSet<>();
+
+ for (int i = 0; i < importList.size(); i++) {
+ BizHospitalImportVo vo = importList.get(i);
+ int rowNo = i + 2; // Excel 行号 (1=表头)
+ try {
+ String hospital = trimToNull(vo.getHospital());
+ if (hospital == null) {
+ throw new ServiceException("医院名称不能为空");
+ }
+
+ // 批内去重 (case-insensitive)
+ String key = hospital.toLowerCase();
+ if (!seenHospital.add(key)) {
+ throw new ServiceException("医院「" + hospital + "」在文件中重复");
+ }
+
+ // DB 去重
+ BizHospital probe = new BizHospital();
+ probe.setHospital(hospital);
+ probe.setProvince(trimToNull(vo.getProvince()));
+ probe.setCity(trimToNull(vo.getCity()));
+ if (bizHospitalMapper.countByTrimmedHospital(probe) > 0) {
+ throw new ServiceException("医院「" + hospital + "」已存在");
+ }
+
+ BizHospital entity = new BizHospital();
+ entity.setHospital(hospital);
+ entity.setProvince(trimToNull(vo.getProvince()));
+ entity.setCity(trimToNull(vo.getCity()));
+ entity.setOriginalHospital(trimToNull(vo.getOriginalHospital()));
+ entity.setHospitalCode(trimToNull(vo.getHospitalCode()));
+ entity.setMilitaryHospital(trimToNull(vo.getMilitaryHospital()));
+ entity.setType(trimToNull(vo.getType()));
+ SnowflakeId.injectIfEmpty(entity, "hospitalId");
+ bizHospitalMapper.insert(entity);
+
+ result.ok();
+ } catch (Exception e) {
+ result.fail(rowNo, e.getMessage() == null ? "导入失败" : e.getMessage());
+ }
+ }
+ return result;
+ }
+
+ /** 除 hospital 外的字段统一 trim (把空串压成 null, 避免脏数据) */
+ private void normalize(BizHospital entity) {
+ entity.setProvince(trimToNull(entity.getProvince()));
+ entity.setCity(trimToNull(entity.getCity()));
+ entity.setOriginalHospital(trimToNull(entity.getOriginalHospital()));
+ entity.setHospitalCode(trimToNull(entity.getHospitalCode()));
+ entity.setMilitaryHospital(trimToNull(entity.getMilitaryHospital()));
+ entity.setType(trimToNull(entity.getType()));
+ }
+
+ private String trimToNull(String s) {
+ if (s == null) return null;
+ String t = s.trim();
+ return t.isEmpty() ? null : t;
+ }
+}
\ No newline at end of file
diff --git a/ry-api/ruoyi-business/src/main/resources/mapper/business/BizHospitalMapper.xml b/ry-api/ruoyi-business/src/main/resources/mapper/business/BizHospitalMapper.xml
new file mode 100644
index 0000000..e673ffd
--- /dev/null
+++ b/ry-api/ruoyi-business/src/main/resources/mapper/business/BizHospitalMapper.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select id, province, city, hospital, original_hospital, hospital_code, military_hospital, type
+ from biz_hospital
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into biz_hospital
+
+ id,
+ province,
+ city,
+ hospital,
+ original_hospital,
+ hospital_code,
+ military_hospital,
+ type,
+
+
+ #{hospitalId},
+ #{province},
+ #{city},
+ #{hospital},
+ #{originalHospital},
+ #{hospitalCode},
+ #{militaryHospital},
+ #{type},
+
+
+
+
+
+ update biz_hospital
+
+ province = #{province},
+ city = #{city},
+ hospital = #{hospital},
+ original_hospital = #{originalHospital},
+ hospital_code = #{hospitalCode},
+ military_hospital = #{militaryHospital},
+ type = #{type},
+
+ where id = #{hospitalId}
+
+
+
+
+ delete from biz_hospital where id in
+
+ #{hid}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ry-vue3/src/api/business/hospital.js b/ry-vue3/src/api/business/hospital.js
new file mode 100644
index 0000000..d95e4db
--- /dev/null
+++ b/ry-vue3/src/api/business/hospital.js
@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+/**
+ * 医院字典 API (biz_hospital)
+ * 基础 CRUD 走 @/api/public 的 bizList/bizGet/bizAdd/bizUpdate/bizDelete (路径 /business/hospital/...)
+ * 本文件只放特殊形状的端点: 远程搜索 / 引用统计 / 导入模板 / 导入 / 导出
+ */
+
+// 医生注册下拉远程搜索 (匿名公开, keyword 为空不返回建议)
+export function searchHospital(keyword) {
+ return request.get('/business/hospital/search', { params: { keyword } })
+}
+
+// 删除前引用统计: 返回医院名被业务表 work_unit 引用的条数
+export function countHospitalUsage(hospital) {
+ return request.get('/business/hospital/countUsage', { params: { hospital } })
+}
+
+// 下载导入模板 (blob)
+export function downloadHospitalTemplate() {
+ return request.get('/business/hospital/importTemplate', { responseType: 'blob' })
+}
+
+// 批量导入 (FormData multipart, 返回 { code, msg, data: ImportResult })
+export function importHospital(file) {
+ const form = new FormData()
+ form.append('file', file)
+ return request({
+ url: '/business/hospital/importData',
+ method: 'post',
+ data: form,
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
+}
+
+// 导出 (跟随当前筛选条件, blob)
+export function exportHospital(params) {
+ return request.post('/business/hospital/export', null, { params, responseType: 'blob' })
+}
diff --git a/ry-vue3/src/views/admin/Hospital.vue b/ry-vue3/src/views/admin/Hospital.vue
new file mode 100644
index 0000000..cbbec25
--- /dev/null
+++ b/ry-vue3/src/views/admin/Hospital.vue
@@ -0,0 +1,387 @@
+
+
+
首页 / 资料库管理 / 医院管理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+ 新增医院
+ 批量导入
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+ {{ militaryLabel(row.militaryHospital) }}
+
+
+
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+ 将文件拖到此处,或点击上传
+
+
+ 仅允许导入 xls、xlsx 格式文件
+ 下载模板
+
+
+ 取 消
+ 确 定
+
+
+
+
+
+
+
+
+