From 0c795a3055d6a0d2ee67e2963f4ba1f21775d2ce Mon Sep 17 00:00:00 2001 From: huwei <3313749341@qq.com> Date: Tue, 7 Jul 2026 17:14:09 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/enterpriseInfo/adapter.js | 738 +++++++++++++++++++++++ src/utils/enterpriseInfo/http.js | 99 +++ src/utils/enterpriseInfo/idUtil.js | 57 ++ src/utils/enterpriseInfo/orgBootstrap.js | 103 ++++ src/utils/enterpriseInfo/orgContext.js | 48 ++ src/utils/qualFiling/personnelHelper.js | 8 + src/utils/regulatorOrgInfo/index.js | 108 ++++ 7 files changed, 1161 insertions(+) create mode 100644 src/utils/enterpriseInfo/adapter.js create mode 100644 src/utils/enterpriseInfo/http.js create mode 100644 src/utils/enterpriseInfo/idUtil.js create mode 100644 src/utils/enterpriseInfo/orgBootstrap.js create mode 100644 src/utils/enterpriseInfo/orgContext.js create mode 100644 src/utils/qualFiling/personnelHelper.js create mode 100644 src/utils/regulatorOrgInfo/index.js diff --git a/src/utils/enterpriseInfo/adapter.js b/src/utils/enterpriseInfo/adapter.js new file mode 100644 index 0000000..a990dce --- /dev/null +++ b/src/utils/enterpriseInfo/adapter.js @@ -0,0 +1,738 @@ +/** 企业信息管理:前后端字段与分页格式适配 */ + +import { formatDate, formatDateTime, normalizeDateValue, toApiDate, toApiDateTime } from "../../utils/dateFormat"; +import { resolveUploadFileIds } from "../../utils/mockUpload"; +import { asId, isIdFieldName, normalizeQueryIds } from "./idUtil"; +import { withOrgId } from "./orgContext"; + +const GENDER_NAME = { 1: "男", 2: "女" }; +const RESIGN_AUDIT_STATUS_NAME = { 0: "未审核", 1: "已审核", 2: "已退回" }; +const ENTERPRISE_STATUS_CODE = { 正常: 1, 停业: 2, 注销: 3 }; +const FILING_TYPE_CODE = { 审核备案: "1", 确认备案: "2" }; +const FILING_RECORD_STATUS_CODE = { 已备案: 1, 未备案: 2 }; +const PERSON_TYPE_CODE = { 基础人员: "1", 专职评价师: "2" }; + +function parseAttachmentUrls(urls, defaultName = "附件.pdf") { + if (!urls) { + return []; + } + const raw = String(urls).trim(); + + // JSON 数组字符串:如 '[{"url":"...","uid":"...","name":"...","status":"done"}]' + if (raw.startsWith("[") && raw.endsWith("]")) { + try { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed)) { + return parsed + .filter((item) => item?.url) + .map((item) => ({ + name: item.name || `${defaultName.replace(".pdf", "")}1`, + fileName: item.fileName || item.name || `${defaultName.replace(".pdf", "")}1`, + url: item.url, + uid: item.uid || undefined, + status: item.status || "done", + })); + } + } catch (_) { + // JSON 解析失败,回退到逗号分隔处理 + } + } + + // 旧格式:逗号分隔的 URL 字符串 + return raw + .split(",") + .map((url) => url.trim()) + .filter(Boolean) + .map((url, index) => ({ + name: `${defaultName.replace(".pdf", "")}${index + 1}.pdf`, + fileName: `${defaultName.replace(".pdf", "")}${index + 1}.pdf`, + url, + })); +} + +/** 分页查询参数:子表 org_id → org_info.id;机构信息管理 getInfo 不走此方法 */ +export function toPageQuery(params = {}, fieldMap = {}) { + const query = { + current: params.pageIndex ?? params.current ?? 1, + size: params.pageSize ?? params.size ?? 10, + }; + Object.entries(fieldMap).forEach(([from, to]) => { + const value = params[from]; + if (value !== undefined && value !== null && value !== "") { + query[to] = isIdFieldName(to) ? asId(value) : value; + } + }); + return withOrgId(normalizeQueryIds(query)); +} + +export function fromPageResponse(res, mapItem) { + if (!res) { + return { success: false, data: [], totalCount: 0 }; + } + const list = Array.isArray(res.data) ? res.data : []; + return { + ...res, + data: mapItem ? list.map(mapItem) : list, + totalCount: res.totalCount ?? res.total ?? list.length, + }; +} + +export function fromSingleResponse(res, mapItem) { + if (!res) { + return { success: false, data: null }; + } + return { + ...res, + data: res.data && mapItem ? mapItem(res.data) : res.data, + }; +} + +function pick(obj, keys) { + const next = {}; + keys.forEach((key) => { + if (obj[key] !== undefined) { + next[key] = isIdFieldName(key) ? asId(obj[key]) : obj[key]; + } + }); + return next; +} + +// ─── 机构信息 ─── + +export function toOrgInfoForm(data = {}) { + return { + id: asId(data.id), + orgName: data.unitName, + creditCode: data.creditCode, + safetyIndustryCategory: data.safetyIndustryCategoryName, + regionCountyName: data.districtName, + regionStreetName: data.townStreet, + regionCommunityName: data.villageCommunity, + longitude: data.longitude, + latitude: data.latitude, + registerAddress: data.registerAddress, + businessAddress: data.businessAddress, + ownershipType: data.ownershipTypeName, + gbIndustryCode: data.economyIndustryCode, + legalRepresentative: data.legalRepresentative, + legalRepPhone: data.legalRepresentativePhone, + principal: data.principalName, + principalPhone: data.principalPhone, + safetyDeptHead: data.safetyDeptManager, + safetyDeptPhone: data.safetyDeptManagerPhone, + safetyVpPhone: data.safetyDeputyPhone, + productionDate: data.productionDate, + businessStatus: data.businessStatusName, + disclosureUrl: data.infoDisclosureUrl, + workplaceArea: data.workplaceArea, + archiveRoomArea: data.archiveRoomArea, + fullTimeEvaluatorCount: data.fulltimeEvaluatorCount, + registeredSafetyEngineerCount: data.registeredEngineerCount, + authStatusCode: data.authStatusCode, + enterpriseStatus: data.enterpriseStatusName, + enterpriseScale: data.enterpriseScaleName, + filingType: data.filingTypeName ?? "确认备案", + filingRecordStatus: data.filingRecordStatusName ?? "未备案", + attachments: parseAttachmentUrls(data.attachmentUrls), + }; +} + +export function fromOrgInfoForm(values = {}, options = {}) { + const { isDraft = false } = options; + const enterpriseStatus = values.enterpriseStatus; + const filingType = values.filingType ?? "确认备案"; + const filingRecordStatus = values.filingRecordStatus ?? "未备案"; + return { + ...pick(values, ["id", "tenantId"]), + unitName: values.orgName, + creditCode: values.creditCode, + safetyIndustryCategoryName: values.safetyIndustryCategory, + districtCode: values.regionCountyName, + districtName: values.regionCountyName, + townStreet: values.regionStreetName, + villageCommunity: values.regionCommunityName, + longitude: values.longitude, + latitude: values.latitude, + registerAddress: values.registerAddress, + businessAddress: values.businessAddress, + ownershipTypeName: values.ownershipType, + economyIndustryCode: values.gbIndustryCode, + legalRepresentative: values.legalRepresentative, + legalRepresentativePhone: values.legalRepPhone, + principalName: values.principal, + principalPhone: values.principalPhone, + safetyDeptManager: values.safetyDeptHead, + safetyDeptManagerPhone: values.safetyDeptPhone, + safetyDeputyPhone: values.safetyVpPhone, + productionDate: values.productionDate, + businessStatusName: values.businessStatus, + infoDisclosureUrl: values.disclosureUrl, + workplaceArea: values.workplaceArea, + archiveRoomArea: values.archiveRoomArea, + fulltimeEvaluatorCount: values.fullTimeEvaluatorCount, + registeredEngineerCount: values.registeredSafetyEngineerCount, + authStatusCode: isDraft ? 0 : 1, + authStatusName: isDraft ? "草稿" : "已提交", + enterpriseStatusCode: ENTERPRISE_STATUS_CODE[enterpriseStatus], + enterpriseStatusName: enterpriseStatus, + enterpriseScaleCode: values.enterpriseScale, + enterpriseScaleName: values.enterpriseScale, + filingTypeCode: FILING_TYPE_CODE[filingType] ?? filingType, + filingTypeName: filingType, + filingRecordStatusCode: FILING_RECORD_STATUS_CODE[filingRecordStatus], + filingRecordStatusName: filingRecordStatus, + attachmentUrls: resolveUploadFileIds(values.attachments), + }; +} + +/** 监管端机构账号:后端 state 0=启用,1=禁用 */ +export function isOrgAccountEnabled(record = {}) { + return Number(record.state) === 0; +} + +export function toOrgAccountRow(data = {}) { + return { + id: asId(data.id), + orgName: data.unitName, + district: data.districtName, + state: data.state, + createTime: formatDate(normalizeDateValue(data.createTime)), + }; +} + +/** 监管端分页查询(不带 orgInfoId;部分筛选项需后端 OrgInfoPageQuery 扩展后生效) */ +export function toRegulatorOrgAccountPageQuery(params = {}) { + const query = { + current: params.pageIndex ?? params.current ?? 1, + size: params.pageSize ?? params.size ?? 10, + }; + if (params.orgName) { + query.unitName = params.orgName; + } + if (params.district) { + query.districtName = params.district; + } + const state = params.state; + if (state !== undefined && state !== null && state !== "" && state !== "全部") { + query.state = Number(state); + } + const range = params.openTimeRange; + if (Array.isArray(range) && range.length === 2 && range[0] && range[1]) { + query.createTimeStart = range[0].format ? range[0].format("YYYY-MM-DD") : range[0]; + query.createTimeEnd = range[1].format ? range[1].format("YYYY-MM-DD") : range[1]; + } + return normalizeQueryIds(query); +} + +/** 监管端已备案机构列表行 */ +export function toRegisteredOrgRow(data = {}) { + return { + id: asId(data.id), + orgName: data.unitName, + registerAddress: data.registerAddress, + serviceEnterpriseCount: 0, + evalProjectCount: 0, + filingType: data.filingTypeName, + filingRecordStatusCode: data.filingRecordStatusCode, + filingRecordStatusName: data.filingRecordStatusName, + }; +} + +/** 监管端已备案机构分页查询 */ +export function toRegisteredOrgPageQuery(params = {}) { + const query = { + current: params.pageIndex ?? params.current ?? 1, + size: params.pageSize ?? params.size ?? 10, + }; + if (params.orgName) { + query.unitName = params.orgName; + } + if (params.registerAddress) { + query.registerAddress = params.registerAddress; + } + const filingTypeCode = params.filingType; + if (filingTypeCode !== undefined && filingTypeCode !== null && filingTypeCode !== "") { + query.filingTypeCode = String(filingTypeCode); + } + const filingRecordStatusCode = params.filingRecordStatus; + if (filingRecordStatusCode !== undefined && filingRecordStatusCode !== null && filingRecordStatusCode !== "") { + query.filingRecordStatusCode = Number(filingRecordStatusCode); + } + return normalizeQueryIds(query); +} + +export function fromRegulatorOrgInfoModify(values = {}, existing = {}) { + const merged = { + ...toOrgInfoForm(existing), + ...values, + id: asId(values.id ?? existing.id), + }; + const payload = fromOrgInfoForm(merged, { isDraft: false }); + return { + ...payload, + id: asId(values.id ?? existing.id), + authStatusCode: existing.authStatusCode ?? payload.authStatusCode, + authStatusName: existing.authStatusName ?? payload.authStatusName, + tenantId: existing.tenantId ?? payload.tenantId, + filingTypeCode: existing.filingTypeCode ?? payload.filingTypeCode, + filingTypeName: existing.filingTypeName ?? payload.filingTypeName, + filingRecordStatusCode: existing.filingRecordStatusCode ?? payload.filingRecordStatusCode, + filingRecordStatusName: existing.filingRecordStatusName ?? payload.filingRecordStatusName, + }; +} + +// ─── 部门 / 岗位 ─── + +export function toDepartmentForm(data = {}) { + return { + id: asId(data.id), + parentId: asId(data.parentId), + deptName: data.deptName, + leaderName: data.managerName, + leaderAccount: data.managerAccount, + deptLevel: data.deptLevelName ?? data.deptLevelCode, + }; +} + +export function fromDepartmentForm(values = {}) { + return { + ...pick(values, ["id", "parentId", "tenantId"]), + deptName: values.deptName, + managerName: values.leaderName, + managerAccount: values.leaderAccount, + deptLevelName: values.deptLevel, + deptLevelCode: values.deptLevel, + }; +} + +export function buildDepartmentTree(departments = []) { + const map = new Map(); + departments.forEach((item) => { + map.set(asId(item.id), { ...toDepartmentForm(item), children: [] }); + }); + const roots = []; + departments.forEach((item) => { + const node = map.get(asId(item.id)); + const parentId = asId(item.parentId); + if (parentId != null && parentId !== "0" && map.has(parentId)) { + map.get(parentId).children.push(node); + } + else { + roots.push(node); + } + }); + return roots; +} + +export function toPositionForm(data = {}) { + return { + id: asId(data.id), + deptId: asId(data.deptId), + deptName: data.deptName, + positionName: data.positionName, + dutyDesc: data.dutyDesc, + remark: data.remark ?? data.remarks, + }; +} + +export function fromPositionForm(values = {}) { + return { + ...pick(values, ["id", "tenantId"]), + deptId: asId(values.deptId), + positionName: values.positionName, + dutyDesc: values.dutyDesc, + remark: values.remark, + }; +} + +// ─── 人员 ─── + +export function toStaffForm(data = {}) { + return { + id: asId(data.id), + staffName: data.userName, + account: data.account, + gender: data.genderCode, + birthDate: data.birthDate, + deptId: asId(data.deptId), + positionId: asId(data.postId), + idCardNo: data.idCardNo, + homeAddress: data.currentAddress, + officeAddress: data.officeAddress, + educationType: data.educationTypeName, + educationLevel: data.educationLevelName, + education: data.educationName ?? data.educationCode, + graduateSchool: data.graduateSchool, + major: data.major, + employmentStatus: data.employmentStatusCode, + personType: data.personTypeName ?? "基础人员", + qualScope: data.qualScope, + professionalLevel: data.professionalLevelName, + evaluatorCertNo: data.evaluatorCertNo, + titleName: data.titleName, + registerEngineerFlag: data.registerEngineerFlag ?? 2, + publications: data.publications, + abilityDeclaration: data.abilityDeclaration, + workExperience: data.workExperience, + proofMaterials: parseAttachmentUrls(data.proofMaterialUrl, "专业能力证明.pdf"), + deptName: data.deptName, + positionName: data.postName ?? data.positionName, + certNames: data.certNames, + }; +} + +export function fromStaffForm(values = {}) { + const genderCode = values.gender; + const personType = values.personType ?? "基础人员"; + const educationType = values.educationType; + const educationLevel = values.educationLevel; + const educationName = educationType && educationLevel + ? `${educationType}-${educationLevel}` + : values.education; + return { + ...pick(values, ["id", "tenantId"]), + userName: values.staffName, + account: values.account, + genderCode, + genderName: GENDER_NAME[genderCode] ?? values.genderName, + birthDate: toApiDate(values.birthDate), + deptId: asId(values.deptId), + postId: asId(values.positionId ?? values.postId), + idCardNo: values.idCardNo, + currentAddress: values.homeAddress, + officeAddress: values.officeAddress, + educationName, + educationCode: educationName, + educationTypeCode: educationType, + educationTypeName: educationType, + educationLevelCode: educationLevel, + educationLevelName: educationLevel, + graduateSchool: values.graduateSchool, + major: values.major, + employmentStatusCode: values.employmentStatus ?? 1, + employmentStatusName: values.employmentStatus === 2 ? "离职" : "在职", + personTypeCode: PERSON_TYPE_CODE[personType] ?? personType, + personTypeName: personType, + qualScope: values.qualScope, + professionalLevelCode: values.professionalLevel, + professionalLevelName: values.professionalLevel, + evaluatorCertNo: values.evaluatorCertNo, + titleName: values.titleName, + registerEngineerFlag: values.registerEngineerFlag ?? 2, + publications: values.publications, + abilityDeclaration: values.abilityDeclaration, + workExperience: values.workExperience, + proofMaterialUrl: resolveUploadFileIds(values.proofMaterials), + }; +} + +// ─── 人员证书 ─── + +export function toStaffCertForm(data = {}) { + const certImgs = data.certAttachmentUrl + ? parseAttachmentUrls(data.certAttachmentUrl, data.certName || "证书附件.jpg") + : data.certImgFiles || []; + return { + id: asId(data.id), + staffId: asId(data.personnelId), + certName: data.certName, + certCategory: data.certCategoryName ?? data.certCategoryCode, + certWorkCategory: data.operationCategoryName ?? data.operationCategoryCode, + certNo: data.certNo, + issueOrg: data.issueOrg, + validStartDate: data.validStartDate, + validEndDate: data.validEndDate, + reviewDate: data.reviewDate, + certImgFiles: certImgs, + certImgs, + }; +} + +export function fromStaffCertForm(values = {}) { + const certAttachmentUrl = values.certAttachmentUrl + || values.certImgs?.[0]?.url + || values.certImgFiles?.[0]?.url; + return { + ...pick(values, ["id", "tenantId"]), + personnelId: asId(values.staffId), + certName: values.certName, + certCategoryName: values.certCategory, + certCategoryCode: values.certCategory, + operationCategoryName: values.certWorkCategory, + operationCategoryCode: values.certWorkCategory, + certNo: values.certNo, + issueOrg: values.issueOrg, + validStartDate: values.validStartDate, + validEndDate: values.validEndDate, + reviewDate: values.reviewDate, + certAttachmentUrl, + }; +} + +// ─── 机构资质证书 ─── + +/** 资质证书 enableFlag(1启用/2禁用) ↔ 前端 enableStatus(1启用/0禁用) */ +export function mapQualificationEnableStatus(flag) { + return Number(flag) === 1 ? 1 : 0; +} + +export function mapQualificationEnableFlag(status) { + return Number(status) === 1 ? 1 : 2; +} + +/** 列表/表单统一判断:优先 enableFlag,兼容 enableStatus */ +export function isQualificationEnabled(record = {}) { + const flag = record.enableFlag ?? record.enableStatus; + return mapQualificationEnableStatus(flag) === 1; +} + +export function toQualificationForm(data = {}) { + const certImgs = data.certImageUrl + ? parseAttachmentUrls(data.certImageUrl, data.certName || "证书图片.jpg") + : data.certImgFiles || []; + return { + id: asId(data.id), + certType: data.licenseTypeName ?? data.licenseTypeCode, + certName: data.certName, + certNo: data.certNo, + issueOrg: data.issueOrg, + issueDate: data.issueDate, + validStartDate: data.validStartDate, + validEndDate: data.validEndDate, + remark: data.remark ?? data.remarks, + enableFlag: data.enableFlag, + enableStatus: mapQualificationEnableStatus(data.enableFlag), + issueDate: formatDate(data.issueDate), + validStartDate: formatDate(data.validStartDate), + validEndDate: formatDate(data.validEndDate), + certImgFiles: certImgs, + certImgs, + }; +} + +export function fromQualificationForm(values = {}) { + const certImageUrl = values.certImageUrl + || values.certImgs?.[0]?.url + || values.certImgFiles?.[0]?.url; + return { + ...pick(values, ["id", "tenantId"]), + licenseTypeName: values.certType, + licenseTypeCode: values.certType, + certName: values.certName, + certNo: values.certNo, + issueOrg: values.issueOrg, + issueDate: toApiDate(values.issueDate), + validStartDate: toApiDate(values.validStartDate ?? values.validDate?.[0]), + validEndDate: toApiDate(values.validEndDate ?? values.validDate?.[1]), + certImageUrl, + remark: values.remark, + enableFlag: mapQualificationEnableFlag(values.enableStatus), + }; +} + +function isQualificationExpireWarning(dateStr) { + if (!dateStr) { + return false; + } + const end = new Date(String(dateStr).replace(/-/g, "/")); + if (Number.isNaN(end.getTime())) { + return false; + } + const diff = end.getTime() - Date.now(); + return diff > 0 && diff < 90 * 24 * 3600 * 1000; +} + +/** classGet 分组 Map → 已备案机构详情「申请清单材料」结构 */ +export function fromQualificationClassGetResponse(res) { + const map = res?.data; + if (!map || typeof map !== "object") { + return []; + } + return Object.entries(map).map(([code, list], index) => { + const items = (Array.isArray(list) ? list : []).map(toQualificationForm); + const scopeName = items[0]?.certType || code || `资质${index + 1}`; + const expireDate = items.reduce((latest, item) => { + const d = item.validEndDate; + if (!d) { + return latest; + } + return !latest || d > latest ? d : latest; + }, ""); + return { + id: code || String(index), + scopeName, + expireDate: expireDate || "-", + expireWarning: isQualificationExpireWarning(expireDate), + materials: items.map((item, idx) => { + const previewUrl = item.certImgFiles?.[0]?.url || item.certImgs?.[0]?.url; + return { + id: item.id || `${code}-${idx}`, + name: item.certName || item.certType || "-", + required: true, + status: previewUrl ? "uploaded" : "pending", + previewUrl, + raw: item, + }; + }), + }; + }); +} + +/** 已备案机构详情人员列表行 */ +export function toRegisteredOrgPersonnelRow(data = {}) { + const form = toStaffForm(data); + return { + id: form.id, + name: form.staffName, + gender: GENDER_NAME[form.gender] || "-", + position: form.positionName, + qualScope: form.qualScope, + education: form.education || form.educationLevel, + registerEngineer: form.registerEngineerFlag === 1 ? 1 : 0, + }; +} + +// ─── 装备 ─── + +/** 装备 enableFlag(1启用/2禁用) ↔ 前端 enableStatus(1启用/0禁用) */ +export function mapEquipEnableStatus(flag) { + return Number(flag) === 1 ? 1 : 0; +} + +export function mapEquipEnableFlag(status) { + return Number(status) === 1 ? 1 : 2; +} + +export function isEquipEnabled(record = {}) { + const flag = record.enableFlag ?? record.enableStatus; + return mapEquipEnableStatus(flag) === 1; +} + +export function toEquipForm(data = {}) { + const instrumentType = data.instrumentTypeName ?? data.instrumentTypeCode; + return { + id: asId(data.id), + instrumentType, + instrumentCategory: instrumentType, + deviceType: data.deviceTypeName ?? data.deviceTypeCode, + deviceName: data.deviceName, + model: data.deviceModel, + flowDesc: data.flowDesc, + manufacturer: data.manufacturer, + minFlow: data.minFlow, + maxFlow: data.maxFlow, + calibrationUnit: data.calibrationUnit, + calibrationInitialValue: data.calibrationInitValue, + onSiteCalibrationType: data.fieldCalibrationTypeName ?? data.fieldCalibrationTypeCode, + isDualChannel: data.dualChannelFlag, + enableFlag: data.enableFlag, + enableStatus: mapEquipEnableStatus(data.enableFlag), + }; +} + +export function fromEquipForm(values = {}) { + return { + ...pick(values, ["id", "tenantId"]), + deviceName: values.deviceName, + deviceModel: values.model, + instrumentTypeName: values.instrumentCategory, + instrumentTypeCode: values.instrumentCategory, + deviceTypeName: values.deviceType, + deviceTypeCode: values.deviceType, + manufacturer: values.manufacturer, + flowDesc: values.flowDesc, + minFlow: values.minFlow, + maxFlow: values.maxFlow, + calibrationUnit: values.calibrationUnit, + calibrationInitValue: values.calibrationInitialValue, + fieldCalibrationTypeName: values.onSiteCalibrationType, + fieldCalibrationTypeCode: values.onSiteCalibrationType, + dualChannelFlag: values.isDualChannel, + enableFlag: mapEquipEnableFlag(values.enableStatus), + }; +} + +// ─── 人员变更 / 离职 ─── + +export function toChangeLogForm(data = {}) { + return { + id: asId(data.id), + staffId: asId(data.personnelId), + changeItem: data.changeItem, + changeTime: formatDateTime(data.changeTime), + createBy: data.operatorName, + }; +} + +export function toResignApplyForm(data = {}) { + const auditStatus = data.auditStatusCode ?? data.auditStatus ?? 0; + return { + id: asId(data.id), + personnelId: asId(data.personnelId), + applicantName: data.applicantName, + applyTime: formatDateTime(data.applyTime), + expectedLeaveDate: formatDate(data.expectedResignDate), + leaveReason: data.resignReason, + remark: data.remark ?? data.remarks, + rejectReason: data.rejectReason, + auditStatus, + auditStatusName: data.auditStatusName ?? RESIGN_AUDIT_STATUS_NAME[auditStatus] ?? "未审核", + attachmentId: data.reportFileUrl, + account: data.account, + deptName: data.deptName, + }; +} + +export function fromResignApplyForm(values = {}) { + return { + ...pick(values, ["id", "tenantId"]), + personnelId: asId(values.personnelId), + applicantName: values.applicantName, + applyTime: toApiDateTime(values.applyTime), + expectedResignDate: toApiDate(values.expectedLeaveDate), + resignReason: values.leaveReason, + remark: values.remark, + reportFileUrl: values.attachmentId ?? values.reportFileUrl, + auditStatusCode: values.auditStatus ?? 0, + auditStatusName: values.auditStatus === 1 ? "已审核" : values.auditStatus === 2 ? "已退回" : "未审核", + }; +} + +export function fromResignAuditForm(values = {}) { + const auditStatus = values.auditStatus; + return { + id: asId(values.id), + personnelId: asId(values.personnelId), + auditStatusCode: auditStatus, + auditStatusName: auditStatus === 1 ? "已审核" : auditStatus === 2 ? "已退回" : "未审核", + rejectReason: values.rejectReason, + }; +} + +export function toStaffChangeSummary(personnel = {}) { + const employmentCode = Number(personnel.employmentStatusCode ?? personnel.employmentStatus ?? 1); + const hasResignApply = personnel.resignApplyId != null && personnel.resignApplyId !== ""; + const resignStatusRaw = personnel.resignAuditStatus; + let resignStatus = null; + if (hasResignApply) { + resignStatus = resignStatusRaw === null || resignStatusRaw === undefined + ? 0 + : Number(resignStatusRaw); + } + return { + id: asId(personnel.id), + staffId: asId(personnel.id), + account: personnel.account, + staffName: personnel.userName ?? personnel.staffName, + deptName: personnel.deptName || "", + employmentStatus: employmentCode, + employmentStatusCode: employmentCode, + employmentStatusName: personnel.employmentStatusName + || (employmentCode === 2 ? "离职" : "在职"), + changeCount: Number(personnel.changeCount ?? 0), + resignApplyId: asId(personnel.resignApplyId), + resignAuditStatus: resignStatus, + resignAuditStatusName: personnel.resignAuditStatusName + ?? (resignStatus !== null ? RESIGN_AUDIT_STATUS_NAME[resignStatus] : undefined), + }; +} diff --git a/src/utils/enterpriseInfo/http.js b/src/utils/enterpriseInfo/http.js new file mode 100644 index 0000000..5d5d441 --- /dev/null +++ b/src/utils/enterpriseInfo/http.js @@ -0,0 +1,99 @@ +import { Get, Post } from "@cqsjjb/jjb-common-lib/http"; +import { normalizeQueryIds, withIds, asId } from "./idUtil"; +import { buildOrgInfoHeaders } from "./orgContext"; + +/** + * jjb-common-lib 在 jjbCommonHttpConfig.header 存在时会完全替换请求头、不再读 sessionStorage。 + * 因此 token / orgInfoId 都通过每次请求的 headers 参数显式传入。 + * + * @param options.includeOrgContext 默认 true;机构 getInfo 查询应传 false(不带 orgInfoId) + */ +function buildRequestHeaders(extraHeaders = {}, options = {}) { + const { includeOrgContext = true } = options; + const headers = { ...extraHeaders }; + const token = sessionStorage.getItem("token"); + if (token && !headers.token) { + headers.token = token; + } + if (!includeOrgContext) { + return headers; + } + return buildOrgInfoHeaders(headers); +} + +function parseHttpError(err) { + const data = err?.response?.data; + if (data && typeof data === "object") { + return { + success: false, + code: data.code ?? data.errCode, + message: data.message || data.errMessage || "操作失败", + data: data.data ?? null, + }; + } + return { success: false, message: err?.message || "请求失败" }; +} + +/** 底层 HTTP,供 adapter 层组合调用(不可嵌套 declareRequest) */ +export async function apiGet(path, params = {}, headers = {}, options = {}) { + try { + return await Get(path, normalizeQueryIds(params), buildRequestHeaders(headers, options)); + } + catch (err) { + return parseHttpError(err); + } +} + +/** @ 前缀表示 Post 请求体不包裹 request 字段 */ +export async function apiPost(path, data = {}, headers = {}, options = {}) { + const url = path.startsWith("@") ? path : `@${path}`; + try { + return await Post(url, withIds(data), buildRequestHeaders(headers, options)); + } + catch (err) { + return parseHttpError(err); + } +} + +export async function apiPostDelete(path, id, options = {}) { + const url = `${path}?id=${encodeURIComponent(asId(id) ?? "")}`; + try { + return await Post(url, {}, buildRequestHeaders({}, options)); + } + catch (err) { + return parseHttpError(err); + } +} + +export function safePageResult(mapper) { + return async (params) => { + try { + const res = await mapper(params); + return res; + } + catch (err) { + console.warn("[enterpriseInfo/safePageResult]", err); + return { success: false, data: [], totalCount: 0 }; + } + }; +} + +export function safeAction(mapper) { + return async (params) => { + try { + const res = await mapper(params); + if (res && res.success === false) { + return { + success: false, + message: res.message || res.msg || "操作失败", + code: res.code, + }; + } + return res; + } + catch (err) { + console.warn("[enterpriseInfo/safeAction]", err); + return parseHttpError(err); + } + }; +} diff --git a/src/utils/enterpriseInfo/idUtil.js b/src/utils/enterpriseInfo/idUtil.js new file mode 100644 index 0000000..16ae3f5 --- /dev/null +++ b/src/utils/enterpriseInfo/idUtil.js @@ -0,0 +1,57 @@ +/** + * 雪花 ID 全局处理:禁止 Number(),统一字符串传递。 + * JS Number.MAX_SAFE_INTEGER = 9007199254740991,雪花 id 超出后会精度丢失。 + */ + +export function asId(value) { + if (value == null || value === "") { + return undefined; + } + return String(value); +} + +/** 是否像主键/外键字段名 */ +export function isIdFieldName(key) { + return key === "id" || key.endsWith("Id"); +} + +/** 对象内所有 *Id / id 字段转字符串 */ +export function withIds(obj = {}) { + if (!obj || typeof obj !== "object") { + return obj; + } + const next = { ...obj }; + Object.keys(next).forEach((key) => { + if (isIdFieldName(key) && next[key] != null && next[key] !== "") { + next[key] = asId(next[key]); + } + }); + return next; +} + +/** 分页/查询参数中的 id 字段转字符串 */ +export function normalizeQueryIds(query = {}) { + const next = { ...query }; + Object.keys(next).forEach((key) => { + if (isIdFieldName(key) && next[key] != null && next[key] !== "") { + next[key] = asId(next[key]); + } + }); + return next; +} + +/** 兼容历史 Number() 精度丢失:比较雪花 id 前 16 位 */ +export function sameId(a, b) { + const sa = asId(a); + const sb = asId(b); + if (!sa || !sb) { + return false; + } + if (sa === sb) { + return true; + } + return sa.slice(0, 16) === sb.slice(0, 16); +} + +/** @deprecated 使用 sameId */ +export const sameDeptId = sameId; diff --git a/src/utils/enterpriseInfo/orgBootstrap.js b/src/utils/enterpriseInfo/orgBootstrap.js new file mode 100644 index 0000000..d98422c --- /dev/null +++ b/src/utils/enterpriseInfo/orgBootstrap.js @@ -0,0 +1,103 @@ +/** + * 浼佷笟淇℃伅妯″潡鍒濆鍖栦笌涓嬫媺鏁版嵁鎷夊彇锛堢粫杩?DVA锛岄伩鍏?loading 鐘舵€佸鑷撮〉闈㈤噸娓叉煋姝诲惊鐜級 + */ + +import { + fromPageResponse, + fromSingleResponse, + toDepartmentForm, + toOrgInfoForm, + toPageQuery, + toPositionForm, +} from "./adapter"; +import { apiGet } from "./http"; +import { clearOrgInfoId, getOrgInfoId, setOrgInfoId } from "./orgContext"; + +/** 鏈烘瀯淇℃伅绠$悊椤佃矾寰勶紙鏃犳満鏋勬暟鎹椂鍞竴鍙闂〉锛?*/ +export const ORG_INFO_PAGE_PATH = "/certificate/container/EnterpriseInfo/OrgInfo"; +export const MATERIAL_REPORT_PAGE_PATH = "/certificate/container/EnterpriseInfo/MaterialReport"; + +let ensureOrgPromise = null; +/** 鏈€杩戜竴娆?getInfo 杞崲缁撴灉锛屼緵鏈烘瀯淇℃伅椤靛鐢紝閬垮厤閲嶅璇锋眰 */ +let lastOrgInfoDetail = null; + +export function isOrgInfoPage(path = window.location.pathname) { + return path === ORG_INFO_PAGE_PATH || path.startsWith(`${ORG_INFO_PAGE_PATH}/`) || path === MATERIAL_REPORT_PAGE_PATH || path.startsWith(`${MATERIAL_REPORT_PAGE_PATH}/`); +} + +export function getOrgInfoDetail() { + return lastOrgInfoDetail; +} + +export function setOrgInfoDetailCache(detail) { + lastOrgInfoDetail = detail; +} + +function fetchOrgInfoContext() { + const cachedBefore = getOrgInfoId(); + return apiGet("/safetyEval/org-info/getInfo", {}, {}, { includeOrgContext: false }) + .then((res) => { + lastOrgInfoDetail = fromSingleResponse(res, toOrgInfoForm); + const id = lastOrgInfoDetail?.data?.id ?? res?.data?.id; + if (id) { + setOrgInfoId(id); + return { hasOrg: true, orgInfoId: String(id), detail: lastOrgInfoDetail }; + } + // 本地联调:getInfo 无 SSO 关联时,保留 session 中已有 orgInfoId + if (cachedBefore) { + return { hasOrg: true, orgInfoId: cachedBefore, detail: lastOrgInfoDetail }; + } + clearOrgInfoId(); + return { hasOrg: false, orgInfoId: null, detail: lastOrgInfoDetail }; + }) + .catch((err) => { + console.warn("[ensureOrgContext] getInfo failed:", err); + clearOrgInfoId(); + lastOrgInfoDetail = { success: false, data: null }; + return { hasOrg: false, orgInfoId: null, detail: lastOrgInfoDetail, networkError: true }; + }); +} + +/** + * 纭繚宸茬紦瀛樻満鏋?id锛堝瓙琛ㄥ垎椤?璇锋眰澶?orgInfoId 渚濊禆姝ゅ€硷級 + * @param {{ force?: boolean }} options force=true 鏃跺拷鐣ョ紦瀛樺苟閲嶆柊 getInfo锛堣繘鍏ヤ紒涓氫俊鎭ā鍧楁椂浣跨敤锛? + * @returns {Promise<{ hasOrg: boolean, orgInfoId: string|null, detail?: object, networkError?: boolean }>} + */ +export function ensureOrgContext(options = {}) { + const { force = false } = options; + + if (!force) { + const cached = getOrgInfoId(); + if (cached) { + return Promise.resolve({ hasOrg: true, orgInfoId: cached, detail: lastOrgInfoDetail }); + } + } + + if (ensureOrgPromise) { + return ensureOrgPromise; + } + + ensureOrgPromise = fetchOrgInfoContext().finally(() => { + ensureOrgPromise = null; + }); + return ensureOrgPromise; +} + +export async function fetchOrgDepartmentPage(params = {}) { + await ensureOrgContext(); + const query = toPageQuery(params, { likeDeptName: "deptName" }); + const res = await apiGet("/safetyEval/org-department/page", query); + return fromPageResponse(res, toDepartmentForm); +} + +export async function fetchOrgPositionPage(params = {}) { + await ensureOrgContext(); + const query = toPageQuery(params, { + eqDeptId: "deptId", + likePositionName: "positionName", + }); + const res = await apiGet("/safetyEval/org-position/page", query); + return fromPageResponse(res, toPositionForm); +} + + diff --git a/src/utils/enterpriseInfo/orgContext.js b/src/utils/enterpriseInfo/orgContext.js new file mode 100644 index 0000000..17e11c6 --- /dev/null +++ b/src/utils/enterpriseInfo/orgContext.js @@ -0,0 +1,48 @@ +/** 当前登录用户关联的机构 id,供企业信息模块接口请求头 orgInfoId 使用 */ + +const ORG_INFO_ID_KEY = "orgInfoId"; + +let memoryOrgInfoId = null; + +export function getOrgInfoId() { + return memoryOrgInfoId || sessionStorage.getItem(ORG_INFO_ID_KEY) || null; +} + +export function setOrgInfoId(id) { + if (id != null && id !== "") { + memoryOrgInfoId = String(id); + sessionStorage.setItem(ORG_INFO_ID_KEY, memoryOrgInfoId); + } +} + +export function clearOrgInfoId() { + memoryOrgInfoId = null; + sessionStorage.removeItem(ORG_INFO_ID_KEY); +} + +/** 分页/查询参数:前端缓存 orgInfoId → 后端字段 orgId */ +export function withOrgId(params = {}) { + if (params.orgId != null && params.orgId !== "") { + return params; + } + const orgInfoId = getOrgInfoId(); + if (orgInfoId) { + return { ...params, orgId: orgInfoId }; + } + return params; +} + +/** 合并业务请求头,仅通过每次请求的 headers 参数传递,不写入 jjbCommonHttpConfig */ +export function buildOrgInfoHeaders(extraHeaders = {}) { + const id = extraHeaders.orgInfoId || getOrgInfoId(); + if (!id) { + return extraHeaders; + } + return { ...extraHeaders, orgInfoId: String(id) }; +} + +// 页面刷新后从 sessionStorage 恢复 +const storedOrgInfoId = sessionStorage.getItem(ORG_INFO_ID_KEY); +if (storedOrgInfoId) { + memoryOrgInfoId = storedOrgInfoId; +} diff --git a/src/utils/qualFiling/personnelHelper.js b/src/utils/qualFiling/personnelHelper.js new file mode 100644 index 0000000..5eb807e --- /dev/null +++ b/src/utils/qualFiling/personnelHelper.js @@ -0,0 +1,8 @@ +import { fromSingleResponse, toStaffForm } from "../enterpriseInfo/adapter"; +import { apiGet } from "../enterpriseInfo/http"; +import { asId } from "../enterpriseInfo/idUtil"; + +export async function fetchOrgPersonnelDetail(params) { + const res = await apiGet("/safetyEval/org-personnel/get", { id: asId(params?.id) }); + return fromSingleResponse(res, toStaffForm); +} diff --git a/src/utils/regulatorOrgInfo/index.js b/src/utils/regulatorOrgInfo/index.js new file mode 100644 index 0000000..590a5f7 --- /dev/null +++ b/src/utils/regulatorOrgInfo/index.js @@ -0,0 +1,108 @@ +import { declareRequest } from "@cqsjjb/jjb-dva-runtime"; +import { + fromPageResponse, + fromQualificationClassGetResponse, + fromRegulatorOrgInfoModify, + fromSingleResponse, + toOrgAccountRow, + toOrgInfoForm, + toRegisteredOrgPageQuery, + toRegisteredOrgPersonnelRow, + toRegisteredOrgRow, + toRegulatorOrgAccountPageQuery, + toStaffCertForm, + toStaffForm, +} from "../enterpriseInfo/adapter"; +import { apiGet, apiPost, apiPostDelete, safeAction, safePageResult } from "../enterpriseInfo/http"; + +const NO_ORG_CONTEXT = { includeOrgContext: false }; + +export const orgAccountList = declareRequest("orgInfoLoading", safePageResult(async (params) => { + const query = toRegulatorOrgAccountPageQuery(params); + const res = await apiGet("/safetyEval/org-info/page", query, {}, NO_ORG_CONTEXT); + return fromPageResponse(res, toOrgAccountRow); +})); + +export const orgAccountGet = declareRequest("orgInfoLoading", safeAction(async ({ id }) => { + const res = await apiGet("/safetyEval/org-info/get", { id }, {}, NO_ORG_CONTEXT); + const result = fromSingleResponse(res, toOrgInfoForm); + return { + ...result, + raw: res?.data ?? null, + }; +})); + +export const orgAccountModify = declareRequest("orgInfoLoading", safeAction(async (values) => { + const { raw, ...formValues } = values; + const payload = fromRegulatorOrgInfoModify(formValues, raw || {}); + const res = await apiPost("/safetyEval/org-info/modify", payload, {}, NO_ORG_CONTEXT); + return fromSingleResponse(res, toOrgInfoForm); +})); + +export const orgAccountDelete = declareRequest("orgInfoLoading", safeAction(async ({ id }) => { + return apiPostDelete("/safetyEval/org-info/delete", id, NO_ORG_CONTEXT); +})); + +export const orgAccountUpdateState = declareRequest("orgInfoLoading", safeAction(async ({ id, state }) => { + return apiPost("/safetyEval/org-info/update-state", { id, state }, {}, NO_ORG_CONTEXT); +})); + +export const orgAccountResetPassword = declareRequest("orgInfoLoading", safeAction(async ({ id }) => { + const url = `/safetyEval/org-info/reset-password?id=${encodeURIComponent(id)}`; + return apiPost(url, {}, {}, NO_ORG_CONTEXT); +})); + +export const registeredOrgList = declareRequest("orgInfoLoading", safePageResult(async (params) => { + const query = toRegisteredOrgPageQuery(params); + const res = await apiGet("/safetyEval/org-info/page", query, {}, NO_ORG_CONTEXT); + return fromPageResponse(res, toRegisteredOrgRow); +})); + +export const registeredOrgGet = declareRequest("orgInfoLoading", safeAction(async ({ id }) => { + const res = await apiGet("/safetyEval/org-info/get", { id }, {}, NO_ORG_CONTEXT); + return fromSingleResponse(res, toOrgInfoForm); +})); + +/** 详情页直调 HTTP,避免 DVA loading 与 useEffect 依赖 dispatch 函数导致反复取消 */ +export async function fetchRegisteredOrgDetail(id) { + const res = await apiGet("/safetyEval/org-info/get", { id }, {}, NO_ORG_CONTEXT); + return fromSingleResponse(res, toOrgInfoForm); +} + +export async function fetchRegisteredOrgQualificationGroups(orgInfoId) { + const res = await apiGet("/safetyEval/org-qualification/classGet", { id: orgInfoId }, {}, NO_ORG_CONTEXT); + if (res?.success === false) { + throw new Error(res?.message || "加载申请材料失败"); + } + return fromQualificationClassGetResponse(res); +} + +export async function fetchRegisteredOrgPersonnelList(orgInfoId) { + const res = await apiGet("/safetyEval/org-personnel/page", { + orgId: orgInfoId, + current: 1, + size: 999, + }, {}, NO_ORG_CONTEXT); + const page = fromPageResponse(res, toRegisteredOrgPersonnelRow); + return page?.data || []; +} + +export async function fetchRegisteredOrgPersonnelDetail({ id }) { + const res = await apiGet("/safetyEval/org-personnel/get", { id }, {}, NO_ORG_CONTEXT); + return fromSingleResponse(res, toStaffForm); +} + +export async function fetchRegisteredOrgPersonnelCertList(personnelId) { + const res = await apiGet("/safetyEval/org-personnel-cert/page", { + personnelId, + current: 1, + size: 999, + }, {}, NO_ORG_CONTEXT); + const page = fromPageResponse(res, toStaffCertForm); + return page?.data || []; +} + +export async function fetchRegisteredOrgPersonnelCertDetail({ id }) { + const res = await apiGet("/safetyEval/org-personnel-cert/get", { id }, {}, NO_ORG_CONTEXT); + return fromSingleResponse(res, toStaffCertForm); +}