diff --git a/docs/db/init.sql b/docs/db/init.sql index b1787883..2430d144 100644 --- a/docs/db/init.sql +++ b/docs/db/init.sql @@ -1216,6 +1216,44 @@ create index idx_org_info_state create index idx_org_info_tenant_id on org_info (tenant_id); +-- ===================================================================== +-- 2026-08-08 变更说明(安全评价在线服务监管平台原型 V1.6 register.html) +-- 变更原因:认证信息填报页相对既有机构信息新增 6 个填报项。 +-- 变更逻辑:在 org_info 表追加 6 个可空列,与 OrgInfoDO/Entity/Cmd/CO 字段对齐。 +-- 变更理由:前后端字段 1:1 还原原型;列均设为可空,必填由前端校验保证(最小侵入,不破坏既有约束)。 +-- 字段对照: +-- qualification_cert_no -> 资质证书编号(原型非必填) +-- fax -> 传真 +-- contact_name_phone -> 联系人及电话 +-- fixed_assets_total -> 固定资产总值(万元,非负) +-- apply_business_scope -> 拟申请的法定安全评价业务范围(多选,逗号分隔) +-- org_intro -> 单位基本情况介绍(长文本) +-- ===================================================================== +alter table org_info + add column qualification_cert_no varchar(100) null comment '资质证书编号(2026-08-08 原型V1.6新增,非必填)', + add column fax varchar(20) null comment '传真(2026-08-08 原型V1.6新增)', + add column contact_name_phone varchar(100) null comment '联系人及电话(2026-08-08 原型V1.6新增)', + add column fixed_assets_total decimal(16, 4) null comment '固定资产总值(万元,非负,2026-08-08 原型V1.6新增)', + add column apply_business_scope varchar(500) null comment '拟申请的法定安全评价业务范围(多选逗号分隔,2026-08-08 原型V1.6新增)', + add column org_intro text null comment '单位基本情况介绍(长文本,2026-08-08 原型V1.6新增)'; + +-- ===================================================================== +-- 2026-08-08 变更说明(拆分"联系人及电话"为两个独立字段) +-- 变更原因:原 contact_name_phone 单字段合并存储"联系人+电话",不便于检索/校验。 +-- 变更逻辑:新增 contact_name(联系人)、contact_phone(联系电话)两列, +-- 保留原 contact_name_phone 旧列不删除(兼容存量历史数据,由后端在写入时不再使用)。 +-- 变更理由:与机构信息填报/展示拆成"联系人""联系电话"两个输入项保持一致。 +-- 字段对照: +-- contact_name -> 联系人 +-- contact_phone -> 联系电话 +-- ===================================================================== +alter table org_info + add column contact_name varchar(50) null comment '联系人(2026-08-08 由 contact_name_phone 拆分新增)', + add column contact_phone varchar(20) null comment '联系电话(2026-08-08 由 contact_name_phone 拆分新增)'; + +-- 说明:历史数据中的 contact_name_phone 列保留不删除,避免既有数据丢失; +-- 后续如需迁移可将 contact_name_phone 拆分为 contact_name/contact_phone 后再清理旧列。 + create table org_personnel ( id bigint not null comment '主键' diff --git a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/OrgInfoExecutor.java b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/OrgInfoExecutor.java index 89402b97..61926ea8 100644 --- a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/OrgInfoExecutor.java +++ b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/OrgInfoExecutor.java @@ -85,6 +85,14 @@ public class OrgInfoExecutor implements OrgInfoApi { entity.setFilingRecordStatusCode(cmd.getFilingRecordStatusCode()); entity.setFilingRecordStatusName(cmd.getFilingRecordStatusName()); entity.setAttachmentUrls(cmd.getAttachmentUrls()); + // 2026-08-08 原型 V1.6 register.html 认证信息填报页新增 6 个字段映射(新增原因/逻辑见 OrgInfoEntity/DO/Cmd) + entity.setQualificationCertNo(cmd.getQualificationCertNo()); + entity.setFax(cmd.getFax()); + entity.setContactName(cmd.getContactName()); + entity.setContactPhone(cmd.getContactPhone()); + entity.setFixedAssetsTotal(cmd.getFixedAssetsTotal()); + entity.setApplyBusinessScope(cmd.getApplyBusinessScope()); + entity.setOrgIntro(cmd.getOrgIntro()); entity.setRegisterUserId(cmd.getRegisterUserId()); entity.setId(cmd.getId()); @@ -144,6 +152,14 @@ public class OrgInfoExecutor implements OrgInfoApi { entity.setFilingRecordStatusCode(cmd.getFilingRecordStatusCode()); entity.setFilingRecordStatusName(cmd.getFilingRecordStatusName()); entity.setAttachmentUrls(cmd.getAttachmentUrls()); + // 2026-08-08 原型 V1.6 register.html 认证信息填报页新增 6 个字段映射 + entity.setQualificationCertNo(cmd.getQualificationCertNo()); + entity.setFax(cmd.getFax()); + entity.setContactName(cmd.getContactName()); + entity.setContactPhone(cmd.getContactPhone()); + entity.setFixedAssetsTotal(cmd.getFixedAssetsTotal()); + entity.setApplyBusinessScope(cmd.getApplyBusinessScope()); + entity.setOrgIntro(cmd.getOrgIntro()); OrgInfoEntity result = orgInfoDomainService.modify(entity); return SingleResponse.success(toCO(result)); @@ -274,6 +290,14 @@ public class OrgInfoExecutor implements OrgInfoApi { co.setFilingRecordStatusCode(entity.getFilingRecordStatusCode()); co.setFilingRecordStatusName(entity.getFilingRecordStatusName()); co.setAttachmentUrls(entity.getAttachmentUrls()); + // 2026-08-08 原型 V1.6 register.html 认证信息填报页新增 6 个字段回显映射 + co.setQualificationCertNo(entity.getQualificationCertNo()); + co.setFax(entity.getFax()); + co.setContactName(entity.getContactName()); + co.setContactPhone(entity.getContactPhone()); + co.setFixedAssetsTotal(entity.getFixedAssetsTotal()); + co.setApplyBusinessScope(entity.getApplyBusinessScope()); + co.setOrgIntro(entity.getOrgIntro()); co.setState(entity.getState()); co.setTenantId(entity.getTenantId()); co.setCreateTime(entity.getCreateTime()); diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/OrgInfoCO.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/OrgInfoCO.java index fd097c6e..a5d96cdc 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/OrgInfoCO.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/OrgInfoCO.java @@ -147,6 +147,31 @@ public class OrgInfoCO { @ApiModelProperty(value = "附件地址列表") private String attachmentUrls; + // ===== 2026-08-08 依据原型 V1.6 register.html 认证信息填报页新增 6 个字段(用于回显)===== + // 变更原因/逻辑/理由同 OrgInfoAddCmd:原型新增资质证书编号、传真、联系人及电话、固定资产总值、拟申请业务范围、单位基本情况介绍。 + // 与 Entity/DO/Cmd 字段保持一致,保证详情查询接口可正确回显。 + + @ApiModelProperty(value = "资质证书编号(原型非必填)") + private String qualificationCertNo; + + @ApiModelProperty(value = "传真") + private String fax; + + @ApiModelProperty(value = "联系人(2026-08-08 由联系人及电话拆分)") + private String contactName; + + @ApiModelProperty(value = "联系电话(2026-08-08 由联系人及电话拆分)") + private String contactPhone; + + @ApiModelProperty(value = "固定资产总值(万元,非负)") + private BigDecimal fixedAssetsTotal; + + @ApiModelProperty(value = "拟申请的法定安全评价业务范围(多选,逗号分隔)") + private String applyBusinessScope; + + @ApiModelProperty(value = "单位基本情况介绍(长文本)") + private String orgIntro; + @ApiModelProperty(value = "状态(0启用1禁用)") private Integer state; diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoAddCmd.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoAddCmd.java index c92e0402..1663653d 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoAddCmd.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoAddCmd.java @@ -3,6 +3,11 @@ package org.qinan.safetyeval.client.dto; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import javax.validation.constraints.Digits; +import javax.validation.constraints.Max; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; @@ -18,10 +23,15 @@ public class OrgInfoAddCmd { /** 主键ID */ private Long id; + // 2026-08-08 与库表 unit_name varchar(200) 对齐,限制长度,避免超长写入被截断/报错 @ApiModelProperty(value = "生产经营单位名称") + @Size(max = 200, message = "单位名称不能超过200字") private String unitName; + // 2026-08-08 与库表 credit_code varchar(18) 对齐:18位且为数字或大写字母 @ApiModelProperty(value = "统一社会信用代码") + @Size(max = 18, message = "统一社会信用代码不能超过18位") + @Pattern(regexp = "^[0-9A-Z]{18}$", message = "统一社会信用代码为18位字母或数字") private String creditCode; @ApiModelProperty(value = "安全生产监管行业类别编码") @@ -48,10 +58,14 @@ public class OrgInfoAddCmd { @ApiModelProperty(value = "纬度") private BigDecimal latitude; + // 2026-08-08 与库表 register_address varchar(500) 对齐 @ApiModelProperty(value = "注册地址") + @Size(max = 500, message = "注册地址不能超过500字") private String registerAddress; + // 2026-08-08 与库表 business_address varchar(500) 对齐 @ApiModelProperty(value = "经营地址") + @Size(max = 500, message = "办公地址不能超过500字") private String businessAddress; @ApiModelProperty(value = "归属类型编码") @@ -60,10 +74,14 @@ public class OrgInfoAddCmd { @ApiModelProperty(value = "归属类型名称") private String ownershipTypeName; + // 2026-08-08 与库表 legal_representative varchar(50) 对齐 @ApiModelProperty(value = "法定代表人") + @Size(max = 50, message = "法定代表人不能超过50字") private String legalRepresentative; + // 2026-08-08 与库表 legal_representative_phone varchar(20) 对齐 @ApiModelProperty(value = "法定代表人联系电话") + @Size(max = 20, message = "法定代表人电话不能超过20字") private String legalRepresentativePhone; @ApiModelProperty(value = "主要负责人") @@ -90,19 +108,29 @@ public class OrgInfoAddCmd { @ApiModelProperty(value = "企业经营状态名称") private String businessStatusName; + // 2026-08-08 与库表 info_disclosure_url varchar(500) 对齐 @ApiModelProperty(value = "信息公开网址") + @Size(max = 500, message = "网址不能超过500字") private String infoDisclosureUrl; + // 与库表 workplace_area decimal(12,2) 对齐:整数最多10位、小数最多2位 @ApiModelProperty(value = "工作场所建筑面积(平方米)") + @Digits(integer = 10, fraction = 2, message = "工作场所建筑面积整数部分最多10位、小数最多2位") private BigDecimal workplaceArea; + // 与库表 archive_room_area decimal(12,2) 对齐:整数最多10位、小数最多2位 @ApiModelProperty(value = "档案室面积(平方米)") + @Digits(integer = 10, fraction = 2, message = "档案室面积整数部分最多10位、小数最多2位") private BigDecimal archiveRoomArea; + // 与库表 fulltime_evaluator_count int(11) 对齐:32位有符号整数上限 @ApiModelProperty(value = "专职安全评价师数量") + @Max(value = 2147483647, message = "专职安全评价师数量超出范围") private Integer fulltimeEvaluatorCount; + // 与库表 registered_engineer_count int(11) 对齐:32位有符号整数上限 @ApiModelProperty(value = "注册安全工程师数量") + @Max(value = 2147483647, message = "注册安全工程师数量超出范围") private Integer registeredEngineerCount; @ApiModelProperty(value = "国民经济行业分类编码") @@ -141,9 +169,51 @@ public class OrgInfoAddCmd { @ApiModelProperty(value = "备案状态名称") private String filingRecordStatusName; + // 2026-08-08 营业执照附件地址;落库字段为 text,此处仅做通用长度约束以防御超长输入 @ApiModelProperty(value = "上传附件地址(多个逗号分隔)") + @Size(max = 5000, message = "附件地址过长") private String attachmentUrls; + // ===== 2026-08-08 依据原型 V1.6 register.html 认证信息填报页新增 6 个字段 ===== + // 变更原因:原型相对既有机构信息新增资质证书编号、传真、联系人及电话、固定资产总值、拟申请业务范围、单位基本情况介绍。 + // 变更逻辑:与 OrgInfoEntity / OrgInfoDO / OrgInfoCO 同步新增,保持入参/落库/出参字段一致。 + // 变更理由:前后端字段对齐,1:1 还原原型;库列可空,必填由前端校验保证(最小侵入)。 + + // 与库表 qualification_cert_no varchar(100) 对齐 + @ApiModelProperty(value = "资质证书编号(原型非必填)") + @Size(max = 100, message = "资质证书编号不能超过100字") + private String qualificationCertNo; + + // 与库表 fax varchar(20) 对齐 + @ApiModelProperty(value = "传真") + @Size(max = 20, message = "传真不能超过20字") + private String fax; + + // 与库表 contact_name varchar(50) 对齐 + @ApiModelProperty(value = "联系人") + @Size(max = 50, message = "联系人不能超过50字") + private String contactName; + + // 与库表 contact_phone varchar(20) 对齐 + @ApiModelProperty(value = "联系电话") + @Size(max = 20, message = "联系电话不能超过20字") + private String contactPhone; + + // 与库表 fixed_assets_total decimal(16,4) 对齐:整数最多12位、小数最多4位,非负 + @ApiModelProperty(value = "固定资产总值(万元,非负)") + @Digits(integer = 12, fraction = 4, message = "固定资产总值整数部分最多12位、小数最多4位") + private BigDecimal fixedAssetsTotal; + + // 与库表 apply_business_scope varchar(500) 对齐 + @ApiModelProperty(value = "拟申请的法定安全评价业务范围(多选,逗号分隔)") + @Size(max = 500, message = "业务范围总字数不能超过500字") + private String applyBusinessScope; + + // 与业务约定 org_intro 长度上限一致(库为 text,前端约束 2000) + @ApiModelProperty(value = "单位基本情况介绍(长文本)") + @Size(max = 2000, message = "单位基本情况介绍不能超过2000字") + private String orgIntro; + @ApiModelProperty(value = "用户注册") private Long registerUserId; diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoModifyCmd.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoModifyCmd.java index 4a7b28df..29e47a53 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoModifyCmd.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/dto/OrgInfoModifyCmd.java @@ -1,6 +1,10 @@ package org.qinan.safetyeval.client.dto; import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.Digits; +import javax.validation.constraints.Max; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; @@ -18,9 +22,12 @@ public class OrgInfoModifyCmd { private Long id; @ApiModelProperty(value = "生产经营单位名称") + @Size(max = 200, message = "单位名称不能超过200字") private String unitName; @ApiModelProperty(value = "统一社会信用代码") + @Size(max = 18, message = "统一社会信用代码不能超过18位") + @Pattern(regexp = "^[0-9A-Z]{18}$", message = "统一社会信用代码为18位字母或数字") private String creditCode; @ApiModelProperty(value = "安全生产监管行业类别编码") @@ -48,9 +55,11 @@ public class OrgInfoModifyCmd { private BigDecimal latitude; @ApiModelProperty(value = "注册地址") + @Size(max = 500, message = "注册地址不能超过500字") private String registerAddress; @ApiModelProperty(value = "经营地址") + @Size(max = 500, message = "办公地址不能超过500字") private String businessAddress; @ApiModelProperty(value = "归属类型编码") @@ -60,9 +69,11 @@ public class OrgInfoModifyCmd { private String ownershipTypeName; @ApiModelProperty(value = "法定代表人") + @Size(max = 50, message = "法定代表人不能超过50字") private String legalRepresentative; @ApiModelProperty(value = "法定代表人联系电话") + @Size(max = 20, message = "法定代表人电话不能超过20字") private String legalRepresentativePhone; @ApiModelProperty(value = "主要负责人") @@ -90,18 +101,23 @@ public class OrgInfoModifyCmd { private String businessStatusName; @ApiModelProperty(value = "信息公开网址") + @Size(max = 500, message = "网址不能超过500字") private String infoDisclosureUrl; @ApiModelProperty(value = "工作场所建筑面积(平方米)") + @Digits(integer = 10, fraction = 2, message = "工作场所建筑面积整数部分最多10位、小数最多2位") private BigDecimal workplaceArea; @ApiModelProperty(value = "档案室面积(平方米)") + @Digits(integer = 10, fraction = 2, message = "档案室面积整数部分最多10位、小数最多2位") private BigDecimal archiveRoomArea; @ApiModelProperty(value = "专职安全评价师数量") + @Max(value = 2147483647, message = "专职安全评价师数量超出范围") private Integer fulltimeEvaluatorCount; @ApiModelProperty(value = "注册安全工程师数量") + @Max(value = 2147483647, message = "注册安全工程师数量超出范围") private Integer registeredEngineerCount; @ApiModelProperty(value = "国民经济行业分类编码") @@ -141,8 +157,41 @@ public class OrgInfoModifyCmd { private String filingRecordStatusName; @ApiModelProperty(value = "上传附件地址(多个逗号分隔)") + @Size(max = 5000, message = "附件地址过长") private String attachmentUrls; + // ===== 2026-08-08 依据原型 V1.6 register.html 认证信息填报页新增 6 个字段 ===== + // 变更原因/逻辑/理由同 OrgInfoAddCmd:原型新增资质证书编号、传真、联系人及电话、固定资产总值、拟申请业务范围、单位基本情况介绍。 + // 库列可空,必填由前端校验保证(最小侵入)。 + + @ApiModelProperty(value = "资质证书编号(原型非必填)") + @Size(max = 100, message = "资质证书编号不能超过100字") + private String qualificationCertNo; + + @ApiModelProperty(value = "传真") + @Size(max = 20, message = "传真不能超过20字") + private String fax; + + @ApiModelProperty(value = "联系人") + @Size(max = 50, message = "联系人不能超过50字") + private String contactName; + + @ApiModelProperty(value = "联系电话") + @Size(max = 20, message = "联系电话不能超过20字") + private String contactPhone; + + @ApiModelProperty(value = "固定资产总值(万元,非负)") + @Digits(integer = 12, fraction = 4, message = "固定资产总值整数部分最多12位、小数最多4位") + private BigDecimal fixedAssetsTotal; + + @ApiModelProperty(value = "拟申请的法定安全评价业务范围(多选,逗号分隔)") + @Size(max = 500, message = "业务范围总字数不能超过500字") + private String applyBusinessScope; + + @ApiModelProperty(value = "单位基本情况介绍(长文本)") + @Size(max = 2000, message = "单位基本情况介绍不能超过2000字") + private String orgIntro; + @ApiModelProperty(value = "租户id") private Long tenantId; } diff --git a/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/entity/OrgInfoEntity.java b/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/entity/OrgInfoEntity.java index eec74535..f7f8d2cf 100644 --- a/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/entity/OrgInfoEntity.java +++ b/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/entity/OrgInfoEntity.java @@ -145,6 +145,36 @@ public class OrgInfoEntity { /** 上传附件地址(多个逗号分隔) */ private String attachmentUrls; + // ===== 以下字段为 2026-08-08 依据"安全评价在线服务监管平台原型 V1.6-register.html"认证信息填报页新增 ===== + // 变更原因:原型页面相对既有机构信息新增了资质证书编号、传真、联系人及电话、固定资产总值、 + // 拟申请的法定安全评价业务范围、单位基本情况介绍 共 6 个填报项。 + // 变更逻辑:与 org_info 表、OrgInfoDO、OrgInfoAddCmd/ModifyCmd、OrgInfoCO 同步新增,保证前后端字段一致。 + // 变更理由:原型 1:1 还原,新增字段均为填报所需;库表侧不设 NOT NULL,必填由前端校验保证(最小侵入)。 + + /** 资质证书编号(原型:认证信息填报页"资质证书编号",非必填) */ + private String qualificationCertNo; + + /** 传真(原型:必填) */ + private String fax; + + /** + * 联系人(2026-08-08 由 contact_name_phone 单字段拆分为 contact_name / contact_phone) + * 备注:原 contact_name_phone 列保留不删除,仅不再写入。 + */ + private String contactName; + + /** 联系电话(2026-08-08 由 contact_name_phone 拆分新增,原型:必填) */ + private String contactPhone; + + /** 固定资产总值(万元)(原型:必填,非负数字) */ + private BigDecimal fixedAssetsTotal; + + /** 拟申请的法定安全评价业务范围(原型:必填,多选,逗号分隔存储) */ + private String applyBusinessScope; + + /** 单位基本情况介绍(原型:必填,可附页,长文本) */ + private String orgIntro; + /** 状态(0启用1禁用) */ private Integer state; diff --git a/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/exception/ErrorCode.java b/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/exception/ErrorCode.java index c6bf67b8..e2cddd5e 100644 --- a/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/exception/ErrorCode.java +++ b/safety-eval-domain/src/main/java/org/qinan/safetyeval/domain/exception/ErrorCode.java @@ -20,6 +20,8 @@ public enum ErrorCode { // ---- 机构信息 ---- ORG_INFO_NOT_FOUND("01-04-001", "机构信息不存在"), + // 2026-08-08 填报页:机构已备案后不允许前端缓存/提交改写填报信息(依据监管驾驶舱口径 filing_record_status_code=1 为已备案) + ORG_INFO_FILING_LOCKED("01-04-002", "机构已备案,不允许修改填报信息"), // ---- 人员信息 ---- ORG_PERSONNEL_NOT_FOUND("01-05-001", "人员信息不存在"), diff --git a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/config/TenantIdConfig.java b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/config/TenantIdConfig.java new file mode 100644 index 00000000..f29a7698 --- /dev/null +++ b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/config/TenantIdConfig.java @@ -0,0 +1,27 @@ +package org.qinan.safetyeval.infrastructure.config; + +import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; + +/** + * 兜底租户id配置注入。 + *

+ * 2026-08-08 变更:将 {@link InsertFieldDefaults} 中硬编码的兜底租户id改为读取配置文件 def.tenant-id, + * 以支持 dev 本地与线上使用不同租户(dev=2069596397920849920,prod=保持原值 2067161116369793024)。 + * 仅当 SSO 无登录上下文(tenantId 为 null)时作为兜底生效,登录态仍使用 SSO 返回的真实租户。 + *

+ */ +@Configuration +public class TenantIdConfig { + + @Value("${def.tenantId:2067161116369793024}") + private long tenantId; + + @PostConstruct + public void init() { + InsertFieldDefaults.setDefaultTenantId(tenantId); + } +} diff --git a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/dataobject/OrgInfoDO.java b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/dataobject/OrgInfoDO.java index d1bbcf63..05950574 100644 --- a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/dataobject/OrgInfoDO.java +++ b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/dataobject/OrgInfoDO.java @@ -8,7 +8,11 @@ import java.time.LocalDate; import java.time.LocalDateTime; /** - * 机构信息数据对象 + * 机构信息数据对象(持久化层,落库表 org_info) + * + *

字段注解说明:每个字段均标注其【对应前端字段名】(原型 register.html 认证信息填报页 / 注册页的 formData 键, + * 前后端采用同名 camelCase 约定),以及库列与长度约束。下划线标注「原型填报页直接录入字段」的, + * 表示由 register.html 页面表单直接提交;其余多为注册页录入、字典填充或 GBS/系统默认字段。

* * @author safety-eval */ @@ -16,62 +20,207 @@ import java.time.LocalDateTime; @TableName("org_info") public class OrgInfoDO { + /** 主键ID(前端不直接录入,由详情接口回填用于更新分支) */ private Long id; + + /** 【原型填报页直接录入】生产经营单位名称;前端字段:unitName;库列 unit_name varchar(200) */ private String unitName; + + /** 【原型填报页直接录入】统一社会信用代码(18位);前端字段:creditCode;库列 credit_code varchar(18) */ private String creditCode; + + /** 安全生产监管行业类别编码(多选);前端字段:safetyIndustryCategoryCode;库列 safety_industry_category_code */ private String safetyIndustryCategoryCode; + + /** 安全生产监管行业类别名称(多选,逗号分隔);前端字段:safetyIndustryCategoryName;库列 safety_industry_category_name */ private String safetyIndustryCategoryName; + + /** 所属县区编码;前端字段:districtCode;库列 district_code */ private String districtCode; + + /** 所属县区名称;前端字段:districtName;库列 district_name */ private String districtName; + + /** 所属镇街道;前端字段:townStreet;库列 town_street */ private String townStreet; + + /** 所属村社区;前端字段:villageCommunity;库列 village_community */ private String villageCommunity; + + /** 经度(地图选点);前端字段:longitude;库列 longitude decimal */ private BigDecimal longitude; + + /** 纬度(地图选点);前端字段:latitude;库列 latitude decimal */ private BigDecimal latitude; + + /** 【原型填报页直接录入】注册地址;前端字段:registerAddress;库列 register_address varchar(500) */ private String registerAddress; + + /** 【原型填报页直接录入】经营/办公地址;前端字段:businessAddress;库列 business_address varchar(500) */ private String businessAddress; + + /** 归属类型编码;前端字段:ownershipTypeCode;库列 ownership_type_code */ private String ownershipTypeCode; + + /** 归属类型名称;前端字段:ownershipTypeName;库列 ownership_type_name */ private String ownershipTypeName; + + /** 法定代表人;前端字段:legalRepresentative;库列 legal_representative varchar(50) */ private String legalRepresentative; + + /** 法定代表人联系电话;前端字段:legalRepresentativePhone;库列 legal_representative_phone varchar(20) */ private String legalRepresentativePhone; + + /** 主要负责人;前端字段:principalName;库列 principal_name */ private String principalName; + + /** 主要负责人联系电话;前端字段:principalPhone;库列 principal_phone */ private String principalPhone; + + /** 安全管理部门负责人;前端字段:safetyDeptManager;库列 safety_dept_manager */ private String safetyDeptManager; + + /** 安全管理部门负责人联系电话;前端字段:safetyDeptManagerPhone;库列 safety_dept_manager_phone */ private String safetyDeptManagerPhone; + + /** 主管安全副总联系电话;前端字段:safetyDeputyPhone;库列 safety_deputy_phone */ private String safetyDeputyPhone; + + /** 投产日期;前端字段:productionDate;库列 production_date date */ private LocalDate productionDate; + + /** 企业经营状态编码;前端字段:businessStatusCode;库列 business_status_code */ private Integer businessStatusCode; + + /** 企业经营状态名称;前端字段:businessStatusName;库列 business_status_name */ private String businessStatusName; + + /** 【原型填报页直接录入】信息公开网址;前端字段:infoDisclosureUrl;库列 info_disclosure_url varchar(500) */ private String infoDisclosureUrl; + + /** 【原型填报页直接录入】工作场所建筑面积(平方米);前端字段:workplaceArea;库列 workplace_area decimal(12,2) */ private BigDecimal workplaceArea; + + /** 【原型填报页直接录入】档案室面积(平方米);前端字段:archiveRoomArea;库列 archive_room_area decimal(12,2) */ private BigDecimal archiveRoomArea; + + /** 【原型填报页直接录入】专职安全评价师数量;前端字段:fulltimeEvaluatorCount;库列 fulltime_evaluator_count int */ private Integer fulltimeEvaluatorCount; + + /** 【原型填报页直接录入】注册安全工程师数量;前端字段:registeredEngineerCount;库列 registered_engineer_count int */ private Integer registeredEngineerCount; + + /** 国民经济行业分类编码;前端字段:economyIndustryCode;库列 economy_industry_code */ private String economyIndustryCode; + + /** 国民经济行业分类名称;前端字段:economyIndustryName;库列 economy_industry_name */ private String economyIndustryName; + + /** 认证状态编码(1填写信息2审核中3通过);前端字段:authStatusCode;库列 auth_status_code */ private Integer authStatusCode; + + /** 认证状态名称;前端字段:authStatusName;库列 auth_status_name */ private String authStatusName; + + /** 企业状态编码(1正常2停业3注销);前端字段:enterpriseStatusCode;库列 enterprise_status_code */ private Integer enterpriseStatusCode; + + /** 企业状态名称;前端字段:enterpriseStatusName;库列 enterprise_status_name */ private String enterpriseStatusName; + + /** 企业规模编码;前端字段:enterpriseScaleCode;库列 enterprise_scale_code */ private String enterpriseScaleCode; + + /** 企业规模名称(大/中/小/微型);前端字段:enterpriseScaleName;库列 enterprise_scale_name */ private String enterpriseScaleName; + + /** 备案类型编码(1审核备案2确认备案);前端字段:filingTypeCode;库列 filing_type_code */ private String filingTypeCode; + + /** 备案类型名称(审核备案/确认备案);前端字段:filingTypeName;库列 filing_type_name */ private String filingTypeName; + + /** 备案状态编码(1已备案2未备案);前端字段:filingRecordStatusCode;库列 filing_record_status_code */ private Integer filingRecordStatusCode; + + /** 备案状态名称;前端字段:filingRecordStatusName;库列 filing_record_status_name */ private String filingRecordStatusName; + + /** 【原型填报页直接录入】营业执照附件地址(多个逗号分隔);前端字段:attachmentUrls;库列 attachment_urls text */ private String attachmentUrls; + + // ===== 2026-08-08 依据原型 V1.6 register.html 认证信息填报页新增 6 个字段 ===== + // 变更原因:原型相对既有机构信息新增资质证书编号、传真、联系人及电话、固定资产总值、拟申请业务范围、单位基本情况介绍。 + // 变更逻辑:与 OrgInfoEntity / Cmd / CO 同步新增;库列可空,必填由前端校验保证(最小侵入)。 + // 变更理由:前后端字段对齐,1:1 还原原型,避免对既有字段/表约束做破坏性改动。 + + /** 【原型填报页直接录入】资质证书编号(非必填);前端字段:qualificationCertNo;库列 qualification_cert_no varchar(100) */ + private String qualificationCertNo; + + /** 【原型填报页直接录入】传真;前端字段:fax;库列 fax varchar(20) */ + private String fax; + + /** + * 【原型填报页直接录入】联系人。 + *

2026-08-08 由 contact_name_phone 单字段拆分为 contact_name / contact_phone 两个独立字段,二者不合并。 + * 前端字段:contactName(对应原型"联系人"输入框);库列 contact_name varchar(50)。 + * 备注:原 contact_name_phone 列保留不删除,仅不再写入。

+ */ + private String contactName; + + /** + * 【原型填报页直接录入】联系电话。 + *

2026-08-08 由 contact_name_phone 拆分新增,与 contactName 为两个独立字段,不合并。 + * 前端字段:contactPhone(对应原型"联系电话"输入框);库列 contact_phone varchar(20)。

+ */ + private String contactPhone; + + /** 【原型填报页直接录入】固定资产总值(万元,非负);前端字段:fixedAssetsTotal;库列 fixed_assets_total decimal(16,4) */ + private BigDecimal fixedAssetsTotal; + + /** 【原型填报页直接录入】拟申请的法定安全评价业务范围(多选,逗号分隔);前端字段:applyBusinessScope;库列 apply_business_scope varchar(500) */ + private String applyBusinessScope; + + /** 【原型填报页直接录入】单位基本情况介绍(长文本);前端字段:orgIntro;库列 org_intro text */ + private String orgIntro; + + /** 记录状态(逻辑删除/状态标记);系统字段,无前端录入字段 */ private Integer state; - // ---- GBS默认字段 ---- + // ---- GBS默认字段(由 GBS 同步/系统注入,无前端录入字段) ---- + /** 删除标记(GBS 默认字段) */ private String deleteEnum; + + /** 备注(GBS 默认字段) */ private String remarks; + + /** 创建人姓名(GBS 默认字段) */ private String createName; + + /** 更新人姓名(GBS 默认字段) */ private String updateName; + + /** 租户ID(GBS 默认字段,见 InsertFieldDefaults 兜底配置) */ private Long tenantId; + + /** 机构ID(GBS 默认字段) */ private Long orgId; + + /** 乐观锁版本(GBS 默认字段) */ private Integer version; + + /** 创建时间(GBS 默认字段) */ private LocalDateTime createTime; + + /** 更新时间(GBS 默认字段) */ private LocalDateTime updateTime; + + /** 创建人ID(GBS 默认字段) */ private Long createId; + + /** 更新人ID(GBS 默认字段) */ private Long updateId; + + /** 环境标识(GBS 默认字段) */ private String env; } diff --git a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/gatewayimpl/OrgInfoGatewayImpl.java b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/gatewayimpl/OrgInfoGatewayImpl.java index 8922cc4e..0388c507 100644 --- a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/gatewayimpl/OrgInfoGatewayImpl.java +++ b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/gatewayimpl/OrgInfoGatewayImpl.java @@ -76,17 +76,38 @@ public class OrgInfoGatewayImpl implements OrgInfoGateway { @Override public OrgInfoEntity save(OrgInfoEntity entity) { + // ===== 2026-08-08 填报页"缓存/提交"新增/更新判定(变更原因) ===== + // 变更逻辑:按 create_id(=registerUserId) 查询该注册用户是否已填报过机构信息。 + // 1) 不存在 -> 新增(下方 insert 分支); + // 2) 已存在且"未备案"(filing_record_status_code != 1) -> 用前端新值覆盖更新(updateById); + // 3) 已存在且"已备案"(filing_record_status_code = 1) -> 拒绝覆盖,抛出 BizException 由上层提示。 + // 变更理由:原实现在"已存在"分支始终 return 旧记录(且 toEntity(aDo) 结果被丢弃), + // 导致缓存/提交后前端新值未落库(数据丢失);现修正为"存在即更新(前提是未备案)"。 + // 备注:filing_record_status_code=1 为"已备案"(依据监管端驾驶舱设计文档口径)。 if (entity.getRegisterUserId() != null) { + // ===== 2026-08-08 保存前校验注册用户是否存在(变更原因) ===== + // 变更逻辑:以 registerUserId 作为 account 主键查询 account 表,若不存在则拒绝保存。 + // 变更理由:原实现对 registerUserId 不做任何校验,前端误传/瞎写非法 id 会直接写进 + // org_info.create_id,造成 create_id 与真实账户脱节、后续按登录用户查不到数据。 + // 备注:registerUserId 的语义即为 account 表主键 id(注册流程写入)。 + AccountDO accountDO = accountMapper.selectById(entity.getRegisterUserId()); + if (accountDO == null) { + throw new BizException(ErrorCode.ACCOUNT_NOT_FOUND); + } List orgInfoDOS = orgInfoMapper.selectList(new LambdaQueryWrapper() .eq(OrgInfoDO::getCreateId, entity.getRegisterUserId()).last("limit 1")); if (!CollectionUtils.isEmpty(orgInfoDOS)) { - if (entity.getId() != null) { - OrgInfoDO aDo = toDO(entity); - InsertFieldDefaults.applyForUpdate(aDo); - orgInfoMapper.updateById(aDo); - toEntity(aDo); + OrgInfoDO existing = orgInfoDOS.get(0); + // 已备案机构不允许前端缓存/提交覆盖填报信息(保留原值) + if (Integer.valueOf(1).equals(existing.getFilingRecordStatusCode())) { + throw new BizException(ErrorCode.ORG_INFO_FILING_LOCKED); } - return toEntity(orgInfoDOS.get(0)); + // 未备案:用前端传入的新值覆盖旧记录并更新 + OrgInfoDO aDo = toDO(entity); + aDo.setId(entity.getId() != null ? entity.getId() : existing.getId()); + InsertFieldDefaults.applyForUpdate(aDo); + orgInfoMapper.updateById(aDo); + return toEntity(aDo); } } OrgInfoDO dataObject = toDO(entity); @@ -389,6 +410,14 @@ public class OrgInfoGatewayImpl implements OrgInfoGateway { dataObject.setFilingRecordStatusCode(entity.getFilingRecordStatusCode()); dataObject.setFilingRecordStatusName(entity.getFilingRecordStatusName()); dataObject.setAttachmentUrls(entity.getAttachmentUrls()); + // 2026-08-08 补齐原型新增 7 个字段的映射(此前漏写导致提交后不落库) + dataObject.setQualificationCertNo(entity.getQualificationCertNo()); + dataObject.setFax(entity.getFax()); + dataObject.setContactName(entity.getContactName()); + dataObject.setContactPhone(entity.getContactPhone()); + dataObject.setFixedAssetsTotal(entity.getFixedAssetsTotal()); + dataObject.setApplyBusinessScope(entity.getApplyBusinessScope()); + dataObject.setOrgIntro(entity.getOrgIntro()); dataObject.setState(entity.getState()); dataObject.setTenantId(entity.getTenantId()); return dataObject; @@ -442,6 +471,14 @@ public class OrgInfoGatewayImpl implements OrgInfoGateway { entity.setFilingRecordStatusCode(dataObject.getFilingRecordStatusCode()); entity.setFilingRecordStatusName(dataObject.getFilingRecordStatusName()); entity.setAttachmentUrls(dataObject.getAttachmentUrls()); + // 2026-08-08 补齐原型新增 7 个字段回设(此前漏写导致详情/编辑回显读不到) + entity.setQualificationCertNo(dataObject.getQualificationCertNo()); + entity.setFax(dataObject.getFax()); + entity.setContactName(dataObject.getContactName()); + entity.setContactPhone(dataObject.getContactPhone()); + entity.setFixedAssetsTotal(dataObject.getFixedAssetsTotal()); + entity.setApplyBusinessScope(dataObject.getApplyBusinessScope()); + entity.setOrgIntro(dataObject.getOrgIntro()); entity.setCreateId(dataObject.getCreateId()); entity.setState(dataObject.getState()); entity.setTenantId(dataObject.getTenantId()); diff --git a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/support/InsertFieldDefaults.java b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/support/InsertFieldDefaults.java index 93c656fa..0da8d4ef 100644 --- a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/support/InsertFieldDefaults.java +++ b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/support/InsertFieldDefaults.java @@ -19,6 +19,19 @@ public final class InsertFieldDefaults { private static final String DELETE_NOT_DELETED = "false"; private static final String DEFAULT_ENV = "prod"; + // ===== 2026-08-08 兜底租户id改为读取配置文件(def.tenant-id),按 profile 区分 ===== + // 变更原因:原硬编码 2067161116369793024L,无法区分 dev/线上环境; + // 现由 TenantIdConfig 在 Spring 启动时注入 def.tenant-id 值(local=2069596397920849920,prod=保持原值)。 + // 变更理由:满足"dev 本地用 2069596397920849920、线上保持原值"的要求; + // 同时保留原值作为代码层兜底默认值,防止配置缺失时 NPE。 + // 备注:仅在 SSO 无登录上下文(tenantId 为 null)时作为兜底生效,登录态仍用 SSO 租户。 + private static volatile long DEFAULT_TENANT_ID = 2067161116369793024L; + + // 由 Spring 配置类注入(见 TenantIdConfig),非 Spring 环境保持代码默认原值 + public static void setDefaultTenantId(long tenantId) { + DEFAULT_TENANT_ID = tenantId; + } + private InsertFieldDefaults() { } @@ -52,7 +65,7 @@ public final class InsertFieldDefaults { } Long tenantId = AuthUserContextAdapter.getCurrentTenantId(); if (tenantId == null) { - tenantId = 2067161116369793024L; + tenantId = DEFAULT_TENANT_ID; } setIfNull(dataObject, "createId", userId); setIfNull(dataObject, "updateId", userId); @@ -145,7 +158,7 @@ public final class InsertFieldDefaults { } Long tenantId = AuthUserContextAdapter.getCurrentTenantId(); if (tenantId == null) { - tenantId = 2067161116369793024L; + tenantId = DEFAULT_TENANT_ID; } setIfNull(dataObject, "createId", userId); setIfNull(dataObject, "updateId", userId); diff --git a/safety-eval-infrastructure/src/main/resources/mapper/OrgInfoMapper.xml b/safety-eval-infrastructure/src/main/resources/mapper/OrgInfoMapper.xml index 308c3b7c..09a7da81 100644 --- a/safety-eval-infrastructure/src/main/resources/mapper/OrgInfoMapper.xml +++ b/safety-eval-infrastructure/src/main/resources/mapper/OrgInfoMapper.xml @@ -50,11 +50,22 @@ + + + + + + + + + - `id`, `unit_name`, `credit_code`, `safety_industry_category_code`, `safety_industry_category_name`, `district_code`, `district_name`, `town_street`, `village_community`, `longitude`, `latitude`, `register_address`, `business_address`, `ownership_type_code`, `ownership_type_name`, `legal_representative`, `legal_representative_phone`, `principal_name`, `principal_phone`, `safety_dept_manager`, `safety_dept_manager_phone`, `safety_deputy_phone`, `production_date`, `business_status_code`, `business_status_name`, `info_disclosure_url`, `workplace_area`, `archive_room_area`, `fulltime_evaluator_count`, `registered_engineer_count`, `economy_industry_code`, `economy_industry_name`, `auth_status_code`, `auth_status_name`, `delete_enum`, `remarks`, `create_name`, `update_name`, `tenant_id`, `org_id`, `version`, `create_time`, `update_time`, `create_id`, `update_id`, `env` + `id`, `unit_name`, `credit_code`, `safety_industry_category_code`, `safety_industry_category_name`, `district_code`, `district_name`, `town_street`, `village_community`, `longitude`, `latitude`, `register_address`, `business_address`, `ownership_type_code`, `ownership_type_name`, `legal_representative`, `legal_representative_phone`, `principal_name`, `principal_phone`, `safety_dept_manager`, `safety_dept_manager_phone`, `safety_deputy_phone`, `production_date`, `business_status_code`, `business_status_name`, `info_disclosure_url`, `workplace_area`, `archive_room_area`, `fulltime_evaluator_count`, `registered_engineer_count`, `economy_industry_code`, `economy_industry_name`, `auth_status_code`, `auth_status_name`, `delete_enum`, `remarks`, `create_name`, `update_name`, `tenant_id`, `org_id`, `version`, `create_time`, `update_time`, `create_id`, `update_id`, `env` + + , `qualification_cert_no`, `fax`, `contact_name`, `contact_phone`, `fixed_assets_total`, `apply_business_scope`, `org_intro` diff --git a/safety-eval-start/src/main/resources/application-local.yml b/safety-eval-start/src/main/resources/application-local.yml index 4a77989e..b534beae 100644 --- a/safety-eval-start/src/main/resources/application-local.yml +++ b/safety-eval-start/src/main/resources/application-local.yml @@ -23,4 +23,6 @@ safety-eval: def: password: a123456 publicKey: 0402df2195296d4062ac85ad766994d73e871b887e18efb9a9a06b4cebc72372869b7da6c347c129dee2b46a0f279ff066b01c76208c2a052af75977c722a2ccee + # 2026-08-08 本地 dev 兜底租户id(仅当 SSO 无登录上下文时生效);线上保持原值见 application-prod.yml + tenantId: 2069596397920849920 diff --git a/safety-eval-start/src/main/resources/application-prod.yml b/safety-eval-start/src/main/resources/application-prod.yml index 53797f6d..97b2b804 100644 --- a/safety-eval-start/src/main/resources/application-prod.yml +++ b/safety-eval-start/src/main/resources/application-prod.yml @@ -17,4 +17,6 @@ dromara: def: password: a123456 - publicKey: 0402df2195296d4062ac85ad766994d73e871b887e18efb9a9a06b4cebc72372869b7da6c347c129dee2b46a0f279ff066b01c76208c2a052af75977c722a2ccee \ No newline at end of file + publicKey: 0402df2195296d4062ac85ad766994d73e871b887e18efb9a9a06b4cebc72372869b7da6c347c129dee2b46a0f279ff066b01c76208c2a052af75977c722a2ccee + # 2026-08-08 线上兜底租户id保持原值不变(仅当 SSO 无登录上下文时生效) + tenantId: 2067161116369793024 \ No newline at end of file