generate_dms_mapping.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. """生成 13 类模板字段 ↔ DMS 模型字段 映射文档。
  2. 来源分三类:
  3. 1. DMS来源 —— DMS 模型中有对应字段(模型.字段)
  4. 2. 源Excel来源(DMS未建模型) —— 源 Excel 中确实存在,但 DMS 没有建模型,
  5. 需人工从源 Excel 填入模板(标注具体文件+列)
  6. 3. 新增融合(图谱关联字段) —— DMS 与源 Excel 均无,为图谱建模/关联额外添加
  7. """
  8. from __future__ import annotations
  9. import json
  10. import re
  11. import sys
  12. from pathlib import Path
  13. sys.stdout.reconfigure(encoding="utf-8")
  14. OUT = Path(r"E:\CODE\knowledge_agent\output\dms_models_fields.json")
  15. DOC = Path(r"E:\CODE\knowledge_agent\docs\DMS字段映射.md")
  16. def norm(s: str) -> str:
  17. return re.sub(r"[\s()()/*、,,::元万㎡条%]", "", s or "")
  18. TEMPLATES: dict[str, list[str]] = {
  19. "项目信息": ["项目编号", "项目名称", "项目简称", "上级项目编号", "续签前项目编号", "项目起止时间",
  20. "省份", "市(区)", "细分业态", "汇总业态", "委托方式", "计费方式", "合同面积",
  21. "合同金额", "年化合同额", "合同额具体信息", "服务期限", "合同状况", "首次服务日期",
  22. "年化合同收入", "客户满意度", "客户投诉", "甲方名称", "项目地址", "项目负责人",
  23. "项目负责人工号", "实际毛利率", "服务状态", "备注"],
  24. "项目月度财务": ["项目编号", "项目名称", "年月", "开票金额", "收款金额", "口径说明",
  25. "财务经办人工号", "财务经办人", "备注"],
  26. "投标记录": ["项目编号", "项目名称", "年份", "投标类型", "中标结果", "中标日期", "投标金额",
  27. "文档路径", "投标经办人工号", "投标经办人", "备注"],
  28. "人员信息": ["姓名", "工号", "组织名称", "岗位名称", "当前服务项目编号", "服务起始年月",
  29. "服务结束年月", "职级", "员工层级", "所属组织名称", "入职日期", "离职日期", "司龄",
  30. "性别", "出生日期", "政治面貌", "联系电话", "备注"],
  31. "考勤与人员财务": ["姓名", "工号", "项目编号", "项目名称", "年月", "1-31日", "上班天数",
  32. "平时加班小时", "国定加班小时", "餐费补助金额", "加班超时费金额",
  33. "国定加班费金额", "值班费金额", "税后工资", "备注"],
  34. "排班明细": ["姓名", "工号", "项目编号", "项目名称", "年月", "1-31日班次", "备注"],
  35. "岗位编制": ["项目编号", "项目名称", "岗位", "预算编制", "项目编制", "标准工时人数", "在岗人数", "备注"],
  36. "设备信息": ["项目编号", "项目名称", "设备编号", "设备类型", "名称", "规格型号参数", "制造厂商",
  37. "安装位置", "出厂日期", "启用日期", "入项目日期", "退项目日期", "出厂编号", "完好状况",
  38. "原值", "设备责任人工号", "设备责任人", "备注"],
  39. "采购与维保": ["记录类型", "项目编号", "项目名称", "供应商名称", "供应商编号", "类别",
  40. "名称/合同名称", "数量", "单位", "单价", "金额", "合同年限", "月份/日期",
  41. "当前状态", "经办人工号", "经办人", "备注"],
  42. "检查记录": ["项目编号", "项目名称", "检查类型", "检查日期", "检查得分", "问题描述",
  43. "整改情况", "整改完成日期", "检查人工号", "检查人", "备注"],
  44. "片区信息": ["片区编号", "片区名称", "片区负责人工号", "片区负责人", "备注"],
  45. "项目片区关系": ["项目编号", "项目名称", "片区编号", "片区名称", "备注"],
  46. "人员证书": ["工号", "姓名", "专业类别", "证书类别", "证书名称", "备注"],
  47. }
  48. NOTES: dict[str, str] = {
  49. "项目信息": "专用模型:sq_project(申勤项目数据) + shenqin_project_annual_stat(年化统计) + sq_project_period(项目期次) + sq_xq(续签表)",
  50. "人员信息": "专用模型:sq_employee(申勤员工)",
  51. "投标记录": "专用模型:shenqin_txb(投续标表) + shenqin_zbb(招标表) + sq_tender_intent/notice/result(招标意向/公告/结果)",
  52. "片区信息": "专用模型:xzbm(行政编码及片区)",
  53. "项目片区关系": "无专用关联表:可基于 sq_project.管理区域 / xzbm 行政编码 推导",
  54. "人员证书": "专用模型:sq_personnel_certificate(AMS枢元/人员证书)",
  55. "考勤与人员财务": "DMS 中未发现考勤专用模型(姓名/工号等为跨模型公共字段)",
  56. "排班明细": "DMS 中未发现排班专用模型",
  57. "项目月度财务": "DMS 无月度开票/收款明细(仅年化/合同金额)",
  58. "岗位编制": "DMS 无编制/在岗模型(sq_project 仅有定岗人数,为预算口径)",
  59. "设备信息": "DMS 中未发现设备台账模型",
  60. "检查记录": "DMS 中未发现品质/安全检查模型",
  61. "采购与维保": "DMS 中未发现采购/维保模型",
  62. }
  63. # 有专用 DMS 模型的模板(其余模板按"源Excel来源"处理)
  64. MODELED = {"项目信息", "人员信息", "投标记录", "片区信息", "人员证书"}
  65. # 字段的源 Excel 说明(DMS 未建模型时)
  66. DEFAULT_SOURCES: dict[str, str] = {
  67. "项目信息": "市场部项目管理表 + 业务大表(表1)",
  68. "项目月度财务": "财务部收费进程表(1-12月物业费/期内开票收款)",
  69. "投标记录": "业务大表表2 中标情况 + 档案目录(中标通知书等)",
  70. "人员信息": "人事部员工信息.xlsx(花名册)",
  71. "考勤与人员财务": "人事部考勤表(如 青浦-赵巷公园-员工岗位考勤表.xls)",
  72. "排班明细": "人事部排班文件(每日班次代码)",
  73. "岗位编制": "运营部 9-项目岗位编制汇总.xlsx",
  74. "设备信息": "运营部设备台账(青浦工业园区/徐汇环境监测中心)",
  75. "采购与维保": "采购部 耗材汇总/维保合同台账/固定资产台账",
  76. "检查记录": "运营部品质巡检及整改记录台账",
  77. "片区信息": "业务大表「管理区域」/ 8-项目信息汇总表",
  78. "项目片区关系": "业务大表「管理区域」推导(M:N)",
  79. "人员证书": "合景悠活职称及资格证书管理登记表",
  80. }
  81. # 源 Excel 字段级说明(DMS 未建模型时,标注到具体文件+列)
  82. SOURCE_EXCEL_NOTES: dict[tuple[str, str], str] = {
  83. ("考勤与人员财务", "工号"): "人事部考勤表「员工编码」列(13 份考勤 xls/xlsx)",
  84. ("考勤与人员财务", "年月"): "考勤表工作表名/表头(如 青浦-赵巷公园-员工岗位考勤表2026年1月-4月员工岗位考勤表.xls →「2026年×月」)",
  85. ("考勤与人员财务", "1-31日"): "考勤表 1~31 日列,班次标记(常/日/夜/日1 等)",
  86. ("考勤与人员财务", "上班天数"): "考勤表「上班天数」列",
  87. ("考勤与人员财务", "平时加班小时"): "考勤表「平时加班小时」列",
  88. ("考勤与人员财务", "国定加班小时"): "考勤表「国定加班小时」列",
  89. ("考勤与人员财务", "餐费补助金额"): "考勤表「餐费补助金额」列",
  90. ("考勤与人员财务", "加班超时费金额"): "考勤表「加班超时费金额」列",
  91. ("考勤与人员财务", "国定加班费金额"): "考勤表「国定加班费金额」列",
  92. ("考勤与人员财务", "值班费金额"): "考勤表「值班费金额」列",
  93. ("考勤与人员财务", "税后工资"): "考勤表末尾金额列(表头为「员工签字」,实际填月度税后工资,如 4896.25)",
  94. ("考勤与人员财务", "备注"): "考勤表「备注」列",
  95. ("排班明细", "工号"): "人事部排班 xlsx「工号(Employee ID)」列",
  96. ("排班明细", "年月"): "排班 xlsx 日期列(2026-01-01 … 2026-01-31),如 排班:青浦-赵巷公园、赵巷文体中心、赵巷镇政府、赵巷品牌公司系统排班(2026年1-4月).xlsx",
  97. ("排班明细", "1-31日班次"): "排班 xlsx 1~31 日「班次代码(Shift Code)」列(OFF/A003/SQ-104/M11-033 等)",
  98. ("岗位编制", "岗位"): "运营部 9-项目岗位编制汇总.xlsx",
  99. ("设备信息", "设备编号"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  100. ("设备信息", "设备类型"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  101. ("设备信息", "规格型号参数"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  102. ("设备信息", "制造厂商"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  103. ("设备信息", "安装位置"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  104. ("设备信息", "出厂日期"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  105. ("设备信息", "启用日期"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  106. ("设备信息", "出厂编号"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  107. ("设备信息", "完好状况"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  108. ("设备信息", "原值"): "运营部设备台账(如 青浦工业园区/徐汇环境监测中心)",
  109. ("检查记录", "检查类型"): "运营部品质巡检及整改记录台账",
  110. ("检查记录", "检查日期"): "运营部品质巡检及整改记录台账",
  111. ("检查记录", "检查得分"): "运营部品质巡检及整改记录台账",
  112. ("检查记录", "问题描述"): "运营部品质巡检及整改记录台账",
  113. ("检查记录", "整改情况"): "运营部品质巡检及整改记录台账",
  114. ("检查记录", "整改完成日期"): "运营部品质巡检及整改记录台账",
  115. ("项目月度财务", "年月"): "财务部收费进程表(1-12月物业费/期内开票收款)",
  116. ("项目月度财务", "开票金额"): "财务部收费进程表(1-12月物业费/期内开票收款)",
  117. ("项目月度财务", "收款金额"): "财务部收费进程表(1-12月物业费/期内开票收款)",
  118. ("项目信息", "客户满意度"): "运营部\\03 客户满意度及投诉记录\\客户满意度\\申勤物业服务满意度评价问卷报告(2025年度).docx → 满意度得分(聚合为项目属性)",
  119. ("项目信息", "客户投诉"): "运营部\\03 客户满意度及投诉记录\\客户投诉\\SHSQ-JGJL-02-0301客户投诉受理单.doc 等 → 投诉条数",
  120. }
  121. # 真正新增融合的字段(DMS 与源 Excel 均无,为图谱建模/关联添加)
  122. FUSION_FIELDS: dict[str, set[str]] = {
  123. "项目信息": {"项目负责人工号", "服务状态", "续签前项目编号"},
  124. "项目月度财务": {"项目名称", "口径说明", "财务经办人工号", "财务经办人"},
  125. "投标记录": {"项目名称", "投标类型", "投标经办人工号", "投标经办人", "中标日期", "文档路径"},
  126. "人员信息": {"当前服务项目编号", "服务起始年月", "服务结束年月", "离职日期"},
  127. "考勤与人员财务": {"项目名称"},
  128. "排班明细": {"项目名称", "备注"},
  129. "岗位编制": {"项目名称"},
  130. "设备信息": {"项目名称", "入项目日期", "退项目日期", "设备责任人工号", "设备责任人"},
  131. "采购与维保": {"记录类型", "项目名称", "供应商编号", "经办人工号", "经办人"},
  132. "检查记录": {"项目名称", "检查人工号", "检查人"},
  133. "片区信息": {"片区编号", "片区负责人工号", "片区负责人"},
  134. "项目片区关系": {"项目名称", "片区名称"},
  135. "人员证书": {"姓名", "备注"},
  136. }
  137. # 不参与 DMS 别名匹配的字段(避免被误匹配到跨模型公共字段,如 sq_employee.c_bz)
  138. EXCLUDE_DMS: set[tuple[str, str]] = {
  139. ("考勤与人员财务", "备注"),
  140. ("排班明细", "备注"),
  141. }
  142. # 语义映射覆盖:模板字段 -> (模型, DMS字段)
  143. OVERRIDES: dict[tuple[str, str], tuple[str, str]] = {
  144. ("人员信息", "工号"): ("sq_employee", "c_ygbm 员工编码"),
  145. ("人员信息", "姓名"): ("sq_employee", "c_xm 姓名"),
  146. ("人员信息", "岗位名称"): ("sq_employee", "c_gwmc 岗位名称"),
  147. ("人员信息", "员工层级"): ("sq_employee", "c_ygcj 员工层级"),
  148. ("人员信息", "职级"): ("sq_employee", "c_zj 职级"),
  149. ("人员信息", "所属组织名称"): ("sq_employee", "c_sszzmc 所属组织名称"),
  150. ("人员信息", "入职日期"): ("sq_employee", "c_rzrq 入职日期"),
  151. ("人员信息", "司龄"): ("sq_employee", "c_sl 司龄"),
  152. ("人员信息", "性别"): ("sq_employee", "c_xb 性别"),
  153. ("人员信息", "出生日期"): ("sq_employee", "c_csrq 出生日期"),
  154. ("人员信息", "政治面貌"): ("sq_employee", "c_zzmm 政治面貌"),
  155. ("人员信息", "联系电话"): ("sq_employee", "c_sjhm 手机号码"),
  156. ("人员信息", "当前服务项目编号"): ("sq_employee", "c_xmmc 项目名称(五级公司)"),
  157. ("项目信息", "项目编号"): ("sq_project", "c_jdbm 金蝶编码"),
  158. ("项目信息", "项目名称"): ("sq_project", "c_xmmc 项目名称"),
  159. ("项目信息", "上级项目编号"): ("sq_project", "c_father_code 父项目编码"),
  160. ("项目信息", "省份"): ("sq_project", "c_sfzxs 省份(直辖市)"),
  161. ("项目信息", "市(区)"): ("sq_project", "c_sq 市(区)"),
  162. ("项目信息", "管理区域"): ("sq_project", "c_glqynbkj 管理区域(内部口径)"),
  163. ("项目信息", "细分业态"): ("sq_project", "c_xfyt 细分业态"),
  164. ("项目信息", "汇总业态"): ("sq_project", "c_hzyt 汇总业态"),
  165. ("项目信息", "委托方式"): ("sq_project", "c_wtfs 委托方式"),
  166. ("项目信息", "计费方式"): ("sq_project", "c_jffs 计费方式"),
  167. ("项目信息", "合同面积"): ("sq_project", "c_htmj 合同面积(平方米)"),
  168. ("项目信息", "合同金额"): ("shenqin_project_annual_stat", "c_contract_amount 合同金额"),
  169. ("项目信息", "年化合同额"): ("shenqin_project_annual_stat", "c_annualized_amount 年化合同额"),
  170. ("项目信息", "合同额具体信息"): ("shenqin_project_annual_stat", "c_current_period_amount/预估/存量/新增"),
  171. ("项目信息", "服务期限"): ("shenqin_project_annual_stat", "c_service_period 服务期限"),
  172. ("项目信息", "合同状况"): ("shenqin_project_annual_stat", "c_contract_status_online/offline"),
  173. ("项目信息", "首次服务日期"): ("sq_project", "c_scfwrq 首次服务日期"),
  174. ("项目信息", "年化合同收入"): ("sq_project", "c_nhhtsr 年化合同收入"),
  175. ("项目信息", "甲方名称"): ("shenqin_project_annual_stat", "c_client_name 甲方名称"),
  176. ("项目信息", "项目地址"): ("shenqin_project_annual_stat", "c_project_address 项目地址"),
  177. ("项目信息", "项目负责人"): ("sq_project", "c_xmdzfzr 项目点状负责人"),
  178. ("项目信息", "实际毛利率"): ("sq_project", "c_xmdnsjmll 项目当年实际毛利率"),
  179. ("项目信息", "项目起止时间"): ("sq_project_period", "c_c_date_range_text 日期区间"),
  180. ("项目信息", "项目简称"): ("shenqin_project_annual_stat", "c_project_short_name 项目简称"),
  181. ("投标记录", "项目编号"): ("shenqin_txb", "c_jdbm 金蝶编码"),
  182. ("投标记录", "投标金额"): ("shenqin_txb", "tbbj 投标报价"),
  183. ("投标记录", "中标结果"): ("sq_tender_result", "c_winning_suppliers 中标供应商名称"),
  184. ("投标记录", "年份"): ("sq_tender_notice", "c_publish_time 发布时间"),
  185. ("片区信息", "片区编号"): ("xzbm", "c_part_code 片区编码"),
  186. ("片区信息", "片区名称"): ("xzbm", "c_part_name 片区名称"),
  187. ("人员证书", "工号"): ("sq_personnel_certificate", "c_c_person_id 人员ID"),
  188. ("人员证书", "证书类别"): ("sq_personnel_certificate", "c_c_cert_type 证书类型"),
  189. }
  190. def load_dms() -> dict[str, dict[str, str]]:
  191. data = json.loads(OUT.read_text(encoding="utf-8"))
  192. aliases: dict[str, str] = {}
  193. for model, info in data.items():
  194. for fname, alias in info.get("fields", []):
  195. key = norm(alias)
  196. if key:
  197. aliases.setdefault(key, f"{model}.{fname}({alias})")
  198. return aliases
  199. def main() -> None:
  200. aliases = load_dms()
  201. lines = ["# DMS 数据库字段映射(13 类模板)", "",
  202. "> 依据 DMS 模型元数据生成(模型:申勤物业数字化 / AMS枢元 相关)。"
  203. "来源分三类:① DMS来源 = 模型.字段(别名);② 源Excel来源(DMS未建模型)"
  204. "= 源 Excel 中确有、但 DMS 无对应模型,需人工从源 Excel 填入;"
  205. "③ 新增融合 = DMS 与源 Excel 均无,为图谱建模/关联额外添加。", ""]
  206. summary = []
  207. for tpl, fields in TEMPLATES.items():
  208. lines.append(f"## {tpl}")
  209. lines.append(f"**说明**:{NOTES.get(tpl, '')}")
  210. lines.append("| 模板字段 | 来源(DMS 模型.字段 / 源Excel文件+列) | 类型 |")
  211. lines.append("|---|---|---|")
  212. found = 0
  213. for f in fields:
  214. ov = OVERRIDES.get((tpl, f))
  215. src = ""
  216. note = ""
  217. if ov:
  218. src = f"{ov[0]}.{ov[1]}"
  219. found += 1
  220. else:
  221. if (tpl, f) not in EXCLUDE_DMS:
  222. for alias, desc in aliases.items():
  223. if norm(f) == alias or (len(norm(f)) >= 2 and norm(f) in alias):
  224. src = desc
  225. found += 1
  226. break
  227. if src:
  228. kind = "DMS来源"
  229. elif f in FUSION_FIELDS.get(tpl, set()):
  230. kind = "新增融合(图谱关联字段)"
  231. note = "DMS 与源 Excel 均无,图谱建模/关联用"
  232. else:
  233. kind = "源Excel来源(DMS未建模型)"
  234. note = SOURCE_EXCEL_NOTES.get((tpl, f)) or DEFAULT_SOURCES.get(tpl, "")
  235. if src:
  236. display = src
  237. elif kind.startswith("源Excel"):
  238. display = f"源Excel:{note}" if note else "—"
  239. else:
  240. display = note or "—"
  241. lines.append(f"| {f} | {display} | {kind} |")
  242. summary.append((tpl, len(fields), found))
  243. lines.append("")
  244. lines.insert(3, "## 覆盖概览")
  245. lines.insert(4, "| 模板 | 字段数 | 有DMS来源 |")
  246. lines.insert(5, "|---|---|---|")
  247. for t, n, f in summary:
  248. lines.insert(6, f"| {t} | {n} | {f} |")
  249. DOC.write_text("\n".join(lines), encoding="utf-8")
  250. import csv
  251. csv_path = Path(r"E:\CODE\knowledge_agent\output\dms_template_mapping.csv")
  252. with csv_path.open("w", newline="", encoding="utf-8-sig") as f:
  253. w = csv.writer(f)
  254. w.writerow(["模板", "模板字段", "DMS来源(模型.字段)", "类型", "说明"])
  255. for tpl, fields in TEMPLATES.items():
  256. for fld in fields:
  257. ov = OVERRIDES.get((tpl, fld))
  258. src = ""
  259. if ov:
  260. src = f"{ov[0]}.{ov[1]}"
  261. else:
  262. if (tpl, fld) not in EXCLUDE_DMS:
  263. for alias, desc in aliases.items():
  264. if norm(fld) == alias or (len(norm(fld)) >= 2 and norm(fld) in alias):
  265. src = desc
  266. break
  267. if src:
  268. kind = "DMS来源"
  269. note = NOTES.get(tpl, "")
  270. elif fld in FUSION_FIELDS.get(tpl, set()):
  271. kind = "新增融合"
  272. note = "图谱建模/关联字段"
  273. else:
  274. kind = "源Excel来源(DMS未建模型)"
  275. note = SOURCE_EXCEL_NOTES.get((tpl, fld)) or DEFAULT_SOURCES.get(tpl, "")
  276. w.writerow([tpl, fld, src, kind, note])
  277. print("覆盖概览:")
  278. for t, n, f in summary:
  279. print(f" {t}: {f}/{n}")
  280. print("文档输出:", DOC)
  281. print("CSV 输出:", csv_path)
  282. if __name__ == "__main__":
  283. main()