Files
DashBoard/tools/generate_documentation.py

351 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
生成 MES 数据库参考文档
用于报表开发参考
"""
import json
from pathlib import Path
from datetime import datetime
# 读取表结构信息
ROOT_DIR = Path(__file__).resolve().parent.parent
DATA_FILE = ROOT_DIR / 'data' / 'table_schema_info.json'
with open(DATA_FILE, 'r', encoding='utf-8') as f:
table_info = json.load(f)
# 表用途描述(根据表名推断)
TABLE_DESCRIPTIONS = {
'DW_MES_LOT_V': 'MES 即時 WIP 視圖 - 批次現況、工站、設備與 Hold 資訊',
'DW_MES_EQUIPMENTSTATUS_WIP_V': '設備狀態與 WIP 關聯視圖 - 即時設備狀態與工單資訊',
'DW_MES_SPEC_WORKCENTER_V': '工站/工序對照視圖 - 用於工站分組與排序映射',
'DW_MES_CONTAINER': '容器/批次主檔 - 目前在製容器狀態、數量與流程資訊',
'DW_MES_HOLDRELEASEHISTORY': 'Hold/Release 歷史表 - 批次停工與解除紀錄',
'DW_MES_JOB': '設備維修工單表 - 維修工單的當前狀態與流程',
'DW_MES_LOTREJECTHISTORY': '批次不良/報廢歷史表 - 不良原因與數量',
'DW_MES_LOTWIPDATAHISTORY': '在製數據採集歷史表 - 製程量測/參數紀錄',
'DW_MES_LOTWIPHISTORY': '在製流轉歷史表 - 批次進出站與流程軌跡',
'DW_MES_MAINTENANCE': '設備保養/維護紀錄表 - 保養計畫與點檢數據',
'DW_MES_PARTREQUESTORDER': '維修用料請求表 - 維修/設備零件請領',
'DW_MES_PJ_COMBINEDASSYLOTS': '併批紀錄表 - 合批/合併批次關聯與數量資訊',
'DW_MES_RESOURCESTATUS': '設備狀態變更歷史表 - 狀態切換與原因',
'DW_MES_RESOURCESTATUS_SHIFT': '設備狀態班次彙總表 - 班次級狀態/工時',
'DW_MES_WIP': '在製品現況表(含歷史累積)- 當前 WIP 狀態/數量',
'DW_MES_HM_LOTMOVEOUT': '批次出站事件歷史表 - 出站/移出交易',
'DW_MES_JOBTXNHISTORY': '維修工單交易歷史表 - 工單狀態變更紀錄',
'DW_MES_LOTMATERIALSHISTORY': '批次物料消耗歷史表 - 用料與批次關聯',
'DW_MES_RESOURCE': '資源表 - 設備/載具等資源基本資料OBJECTCATEGORY=ASSEMBLY 時RESOURCENAME 為設備編號)',
'ERP_WIP_MOVETXN': 'ERP 工單移轉與報廢明細 - 工單層級移轉/報廢數量事件',
'ERP_WIP_MOVETXN_DETAIL': 'ERP 工單站點損耗明細 - 站點/產品維度移轉與報廢明細',
'ERP_PJ_WIP_SCRAP_REASONS_EXCLUDE': 'ERP 報廢原因排除清單 - 控制需排除的報廢原因代碼',
}
# 常见字段说明
COMMON_FIELD_NOTES = {
'ID': '唯一标识符',
'NAME': '名称',
'STATUS': '状态',
'TIMESTAMP': '时间戳',
'CREATEDATE': '创建日期',
'UPDATEDATE': '更新日期',
'LOTID': '批次ID',
'CONTAINERID': '容器ID',
'RESOURCEID': '资源ID',
'EQUIPMENTID': '设备ID',
'OPERATIONID': '工序ID',
'JOBID': '工单ID',
'PRODUCTID': '产品ID',
'CUSTOMERID': '客户ID',
'QTY': '数量',
'QUANTITY': '数量'
}
def generate_markdown():
"""生成 Markdown 文档"""
md = []
# 标题和简介
md.append("# MES 数据库报表开发参考文档\n")
md.append(f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
md.append("---\n")
# 目录
md.append("## 目录\n")
md.append("1. [数据库连接信息](#数据库连接信息)")
md.append("2. [数据库概览](#数据库概览)")
md.append("3. [表结构详细说明](#表结构详细说明)")
md.append("4. [报表开发注意事项](#报表开发注意事项)")
md.append("5. [常用查询示例](#常用查询示例)\n")
md.append("---\n")
# 1. 数据库连接信息
md.append("## 数据库连接信息\n")
md.append("### 连接参数\n")
md.append("| 参数 | 值 |")
md.append("|------|------|")
md.append("| 数据库类型 | Oracle Database 19c Enterprise Edition |")
md.append("| 主机地址 | 請參考 .env 檔案 (DB_HOST) |")
md.append("| 端口 | 請參考 .env 檔案 (DB_PORT) |")
md.append("| 服务名 | 請參考 .env 檔案 (DB_SERVICE) |")
md.append("| 用户名 | 請參考 .env 檔案 (DB_USER) |")
md.append("| 密码 | 請參考 .env 檔案 (DB_PASSWORD) |\n")
md.append("### Python 连接示例\n")
md.append("```python")
md.append("import os")
md.append("import oracledb")
md.append("from dotenv import load_dotenv")
md.append("")
md.append("# 載入環境變數")
md.append("load_dotenv()")
md.append("")
md.append("# 连接配置 (從環境變數讀取)")
md.append("DB_CONFIG = {")
md.append(" 'user': os.getenv('DB_USER'),")
md.append(" 'password': os.getenv('DB_PASSWORD'),")
md.append(" 'dsn': f\"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={os.getenv('DB_HOST')})(PORT={os.getenv('DB_PORT')})))(CONNECT_DATA=(SERVICE_NAME={os.getenv('DB_SERVICE')})))\"")
md.append("}")
md.append("")
md.append("# 建立连接")
md.append("connection = oracledb.connect(**DB_CONFIG)")
md.append("cursor = connection.cursor()")
md.append("")
md.append("# 执行查询")
md.append("cursor.execute('SELECT * FROM DW_MES_WIP WHERE ROWNUM <= 10')")
md.append("results = cursor.fetchall()")
md.append("")
md.append("# 关闭连接")
md.append("cursor.close()")
md.append("connection.close()")
md.append("```\n")
md.append("### JDBC 连接字符串\n")
md.append("```")
md.append("jdbc:oracle:thin:@${DB_HOST}:${DB_PORT}:${DB_SERVICE}")
md.append("```\n")
# 2. 数据库概览
md.append("---\n")
md.append("## 数据库概览\n")
md.append("### 表统计信息\n")
md.append("| # | 表名 | 用途 | 数据量 |")
md.append("|---|------|------|--------|")
for idx, (table_name, info) in enumerate(sorted(table_info.items()), 1):
if 'error' not in info:
row_count = f"{info['row_count']:,}"
description = TABLE_DESCRIPTIONS.get(table_name, '待补充')
md.append(f"| {idx} | `{table_name}` | {description} | {row_count} |")
md.append("")
# 计算总数据量
total_rows = sum(info['row_count'] for info in table_info.values() if 'error' not in info)
md.append(f"**总数据量**: {total_rows:,}\n")
# 3. 表结构详细说明
md.append("---\n")
md.append("## 表结构详细说明\n")
for table_name in sorted(table_info.keys()):
info = table_info[table_name]
if 'error' in info:
continue
md.append(f"### {table_name}\n")
# 表说明
md.append(f"**用途**: {TABLE_DESCRIPTIONS.get(table_name, '待补充')}\n")
md.append(f"**数据量**: {info['row_count']:,}\n")
if info.get('table_comment'):
md.append(f"**表注释**: {info['table_comment']}\n")
# 字段列表
md.append("#### 字段列表\n")
md.append("| # | 字段名 | 数据类型 | 长度 | 可空 | 说明 |")
md.append("|---|--------|----------|------|------|------|")
schema = info.get('schema', [])
for col in schema:
col_num = col['column_id']
col_name = col['column_name']
# 构建数据类型显示
if col['data_type'] in ['VARCHAR2', 'CHAR']:
data_type = f"{col['data_type']}({col['data_length']})"
elif col['data_type'] == 'NUMBER' and col['data_precision']:
if col['data_scale']:
data_type = f"NUMBER({col['data_precision']},{col['data_scale']})"
else:
data_type = f"NUMBER({col['data_precision']})"
else:
data_type = col['data_type']
nullable = "" if col['nullable'] == 'Y' else ""
# 获取字段说明
column_comments = info.get('column_comments', {})
comment = column_comments.get(col_name, '')
# 如果没有注释,尝试从常见字段说明中获取
if not comment:
for key, value in COMMON_FIELD_NOTES.items():
if key in col_name:
comment = value
break
md.append(f"| {col_num} | `{col_name}` | {data_type} | {col.get('data_length', '-')} | {nullable} | {comment} |")
md.append("")
# 索引信息
indexes = info.get('indexes', [])
if indexes:
md.append("#### 索引\n")
md.append("| 索引名 | 类型 | 字段 |")
md.append("|--------|------|------|")
for idx_info in indexes:
idx_type = "唯一索引" if idx_info[1] == 'UNIQUE' else "普通索引"
md.append(f"| `{idx_info[0]}` | {idx_type} | {idx_info[2]} |")
md.append("")
md.append("---\n")
# 4. 报表开发注意事项
md.append("## 报表开发注意事项\n")
md.append("### 性能优化建议\n")
md.append("1. **大数据量表查询优化**")
md.append(" - 以下表数据量较大,查询时务必添加时间范围限制:")
large_tables = [(name, info['row_count']) for name, info in table_info.items()
if 'error' not in info and info['row_count'] > 10000000]
large_tables.sort(key=lambda x: x[1], reverse=True)
for table_name, count in large_tables:
md.append(f" - `{table_name}`: {count:,}")
md.append("")
md.append("2. **索引使用**")
md.append(" - 查询时尽量使用已建立索引的字段作为查询条件")
md.append(" - 避免在索引字段上使用函数,会导致索引失效")
md.append("")
md.append("3. **连接池配置**")
md.append(" - 建议使用连接池管理数据库连接")
md.append(" - 推荐连接池大小5-10 个连接")
md.append("")
md.append("4. **查询超时设置**")
md.append(" - 建议设置查询超时时间为 30-60 秒")
md.append(" - 避免长时间运行的查询影响系统性能")
md.append("")
md.append("### 数据时效性\n")
md.append("- **实时数据表**: `DW_MES_WIP`(含歷史累積), `DW_MES_RESOURCESTATUS`")
md.append("- **历史数据表**: 带有 `HISTORY` 后缀的表")
md.append("- **主数据表**: `DW_MES_RESOURCE`, `DW_MES_CONTAINER`")
md.append("")
md.append("### 常用时间字段\n")
md.append("大多数历史表包含以下时间相关字段:")
md.append("- `CREATEDATE` / `CREATETIMESTAMP`: 记录创建时间")
md.append("- `UPDATEDATE` / `UPDATETIMESTAMP`: 记录更新时间")
md.append("- `TRANSACTIONDATE`: 交易发生时间")
md.append("")
md.append("### 数据权限\n")
md.append("- 當前帳號為唯讀帳號 (詳見 .env 中的 DB_USER)")
md.append("- 仅可执行 SELECT 查询")
md.append("- 无法进行 INSERT, UPDATE, DELETE 操作")
md.append("")
# 5. 常用查询示例
md.append("---\n")
md.append("## 常用查询示例\n")
md.append("### 1. 查询当前在制品数量\n")
md.append("```sql")
md.append("SELECT COUNT(*) as WIP_COUNT")
md.append("FROM DW_MES_WIP")
md.append("WHERE CURRENTSTATUSID IS NOT NULL;")
md.append("```\n")
md.append("### 2. 查询设备状态统计\n")
md.append("```sql")
md.append("SELECT")
md.append(" CURRENTSTATUSID,")
md.append(" COUNT(*) as COUNT")
md.append("FROM DW_MES_RESOURCESTATUS")
md.append("GROUP BY CURRENTSTATUSID")
md.append("ORDER BY COUNT DESC;")
md.append("```\n")
md.append("### 3. 查询最近 7 天的批次历史\n")
md.append("```sql")
md.append("SELECT *")
md.append("FROM DW_MES_LOTWIPHISTORY")
md.append("WHERE CREATEDATE >= SYSDATE - 7")
md.append("ORDER BY CREATEDATE DESC;")
md.append("```\n")
md.append("### 4. 查询工单完成情况\n")
md.append("```sql")
md.append("SELECT")
md.append(" JOBID,")
md.append(" JOBSTATUS,")
md.append(" COUNT(*) as COUNT")
md.append("FROM DW_MES_JOB")
md.append("GROUP BY JOBID, JOBSTATUS")
md.append("ORDER BY JOBID;")
md.append("```\n")
md.append("### 5. 按日期统计生产数量\n")
md.append("```sql")
md.append("SELECT")
md.append(" TRUNC(CREATEDATE) as PRODUCTION_DATE,")
md.append(" COUNT(*) as LOT_COUNT")
md.append("FROM DW_MES_HM_LOTMOVEOUT")
md.append("WHERE CREATEDATE >= SYSDATE - 30")
md.append("GROUP BY TRUNC(CREATEDATE)")
md.append("ORDER BY PRODUCTION_DATE DESC;")
md.append("```\n")
md.append("### 6. 联表查询示例(批次与容器)\n")
md.append("```sql")
md.append("SELECT")
md.append(" w.LOTID,")
md.append(" w.CONTAINERNAME,")
md.append(" c.CURRENTSTATUSID,")
md.append(" c.CUSTOMERID")
md.append("FROM DW_MES_WIP w")
md.append("LEFT JOIN DW_MES_CONTAINER c ON w.CONTAINERID = c.CONTAINERID")
md.append("WHERE w.CREATEDATE >= SYSDATE - 1")
md.append("ORDER BY w.CREATEDATE DESC;")
md.append("```\n")
md.append("---\n")
md.append("## 附录\n")
md.append("### 文档更新记录\n")
md.append(f"- {datetime.now().strftime('%Y-%m-%d')}: 初始版本创建")
md.append("")
md.append("### 联系方式\n")
md.append("如有疑问或需要补充信息,请联系数据库管理员。\n")
return '\n'.join(md)
if __name__ == "__main__":
print("Generating documentation...")
markdown_content = generate_markdown()
output_file = ROOT_DIR / 'docs' / 'MES_Database_Reference.md'
with open(output_file, 'w', encoding='utf-8') as f:
f.write(markdown_content)
print(f"[OK] Documentation generated: {output_file}")