chore: initialize migration workspace

This commit is contained in:
2026-07-02 10:38:13 +08:00
commit 705aec6ab2
44 changed files with 5988 additions and 0 deletions

View File

@@ -0,0 +1,261 @@
# -*- coding: utf-8 -*-
from pathlib import Path
import math
from PIL import Image, ImageDraw, ImageFont
BASE_DIR = Path(__file__).resolve().parent
OUT = BASE_DIR / "MESCommonBase流程图.png"
W, H = 5200, 3800
img = Image.new("RGB", (W, H), "#F7F9FC")
draw = ImageDraw.Draw(img)
FONT_CANDIDATES = [
r"C:\Windows\Fonts\NotoSansSC-VF.ttf",
r"C:\Windows\Fonts\simhei.ttf",
r"C:\Windows\Fonts\Deng.ttf",
r"C:\Windows\Fonts\simsun.ttc",
]
def load_font(size, bold=False):
candidates = []
if bold:
candidates.extend([r"C:\Windows\Fonts\Dengb.ttf", r"C:\Windows\Fonts\simhei.ttf"])
candidates.extend(FONT_CANDIDATES)
for path in candidates:
try:
return ImageFont.truetype(path, size=size)
except Exception:
pass
return ImageFont.load_default()
font_title = load_font(72, True)
font_subtitle = load_font(34)
font_box = load_font(31)
font_box_small = load_font(27)
font_panel_title = load_font(42, True)
font_note = load_font(28)
font_edge = load_font(26)
def text_size(text, font):
bbox = draw.textbbox((0, 0), text, font=font)
return bbox[2] - bbox[0], bbox[3] - bbox[1]
def wrap_line(line, font, max_width):
if not line:
return [""]
lines = []
current = ""
for ch in line:
test = current + ch
if text_size(test, font)[0] <= max_width or not current:
current = test
else:
lines.append(current)
current = ch
if current:
lines.append(current)
return lines
def wrap_text(text, font, max_width):
lines = []
for raw in text.split("\n"):
lines.extend(wrap_line(raw, font, max_width))
return lines
def draw_multiline_center(text, cx, cy, max_width, font, fill="#172033", line_gap=12):
lines = wrap_text(text, font, max_width)
heights = [text_size(line, font)[1] for line in lines]
total_h = sum(heights) + line_gap * (len(lines) - 1)
y = cy - total_h / 2
for line, h in zip(lines, heights):
width, _ = text_size(line, font)
draw.text((cx - width / 2, y), line, font=font, fill=fill)
y += h + line_gap
def draw_multiline_left(text, x, y, max_width, font, fill="#172033", line_gap=10):
lines = wrap_text(text, font, max_width)
yy = y
for line in lines:
draw.text((x, yy), line, font=font, fill=fill)
yy += text_size(line, font)[1] + line_gap
return yy
def box_bounds(cx, cy, w, h):
return (cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2)
def draw_box(cx, cy, w, h, text, fill="#FFFFFF", outline="#2E5A8A", width=5, font=None, radius=34):
font = font or font_box
bounds = box_bounds(cx, cy, w, h)
draw.rounded_rectangle(bounds, radius=radius, fill=fill, outline=outline, width=width)
draw_multiline_center(text, cx, cy, w - 70, font)
return bounds
def draw_diamond(cx, cy, w, h, text, fill="#FFF4CC", outline="#A66F00", width=5, font=None):
font = font or font_box
points = [(cx, cy - h / 2), (cx + w / 2, cy), (cx, cy + h / 2), (cx - w / 2, cy)]
draw.polygon(points, fill=fill, outline=outline)
for i in range(width):
offset = i * 0.9
border_points = [
(cx, cy - h / 2 + offset),
(cx + w / 2 - offset, cy),
(cx, cy + h / 2 - offset),
(cx - w / 2 + offset, cy),
]
draw.line(border_points + [border_points[0]], fill=outline, width=1)
draw_multiline_center(text, cx, cy, w - 80, font)
return points
def draw_panel(x, y, w, h, title, fill="#FFFFFF", outline="#B9C4D6"):
draw.rounded_rectangle((x, y, x + w, y + h), radius=34, fill=fill, outline=outline, width=5)
draw.text((x + 45, y + 28), title, font=font_panel_title, fill="#172033")
def arrow(points, color="#344054", width=6, label=None, label_pos=None):
for p1, p2 in zip(points, points[1:]):
draw.line((p1[0], p1[1], p2[0], p2[1]), fill=color, width=width)
x1, y1 = points[-2]
x2, y2 = points[-1]
angle = math.atan2(y2 - y1, x2 - x1)
size = 24
left = (x2 - size * math.cos(angle - math.pi / 7), y2 - size * math.sin(angle - math.pi / 7))
right = (x2 - size * math.cos(angle + math.pi / 7), y2 - size * math.sin(angle + math.pi / 7))
draw.polygon([(x2, y2), left, right], fill=color)
if label:
lx, ly = label_pos if label_pos else ((x1 + x2) / 2, (y1 + y2) / 2)
tw, th = text_size(label, font_edge)
draw.rounded_rectangle(
(lx - tw / 2 - 14, ly - th / 2 - 8, lx + tw / 2 + 14, ly + th / 2 + 8),
radius=12,
fill="#F7F9FC",
)
draw.text((lx - tw / 2, ly - th / 2), label, font=font_edge, fill=color)
def draw_title():
main_title = "MESCommonBase.ashx 主流程图"
subtitle = "入口解析 -> Type 分发 -> DataLink/ExcelWebCall 执行 -> 响应输出"
tw, _ = text_size(main_title, font_title)
draw.text(((W - tw) / 2, 45), main_title, font=font_title, fill="#111827")
tw, _ = text_size(subtitle, font_subtitle)
draw.text(((W - tw) / 2, 132), subtitle, font=font_subtitle, fill="#475467")
def draw_top_flow():
cx = W / 2
draw_box(cx, 285, 900, 125, "HTTP 请求\nMES_Manage/submit/MESCommonBase.ashx", "#E8F1FF", "#2864B4", font=font_box)
draw_box(cx, 475, 1320, 145, "ProcessRequest\n设置 application/json读取 Request.InputStream", "#E8F1FF", "#2864B4", font=font_box)
draw_box(cx, 670, 1320, 145, "JsonMapper.ToObject(stream)\n尝试读取 Type / type", "#EAF7EF", "#227A45", font=font_box)
draw_box(cx, 865, 1520, 145, "JavaScriptSerializer.Deserialize<jsonobj>(stream)\n成功后用 dataobj.Type 覆盖 type", "#EAF7EF", "#227A45", font=font_box)
draw_diamond(cx, 1090, 520, 190, "dataobj\n== null?", "#FFF4CC", "#B27700", font=font_box)
draw_box(3890, 1090, 840, 130, '读取 Request["param"]\n再次解析 Type / type', "#FFF7E6", "#B27700", font=font_box_small)
draw_diamond(cx, 1315, 500, 180, "switch\n(type)", "#FFF4CC", "#B27700", font=font_box)
arrow([(cx, 348), (cx, 402)])
arrow([(cx, 548), (cx, 598)])
arrow([(cx, 742), (cx, 792)])
arrow([(cx, 938), (cx, 995)])
arrow([(cx + 260, 1090), (3470, 1090)], label="", label_pos=(3140, 1050))
arrow([(3890, 1155), (3890, 1240), (2850, 1240), (2850, 1315)])
arrow([(cx, 1185), (cx, 1225)], label="", label_pos=(2555, 1210))
def draw_branch_panel():
panel_x, panel_y, panel_w, panel_h = 160, 1460, 4880, 1120
draw_panel(panel_x, panel_y, panel_w, panel_h, "Type 分发分支", fill="#FFFFFF", outline="#C7D2E5")
arrow([(W / 2, 1405), (W / 2, panel_y + 10)])
card_w, card_h = 1080, 330
xs = [790, 1960, 3130, 4300]
y1, y2 = 1740, 2205
cards = [
(xs[0], y1, "2001 Excel 导出\nExcelWebCall.ExcelFile(jsonData)\n生成 bytes / fileName / extension\n响应:二进制下载", "#EAF7EF", "#227A45"),
(xs[1], y1, "2002 PDF 导出\nExcelWebCall.ExcelFilePdf(jsonData)\nExcel 转 PDF\n响应:二进制下载", "#EAF7EF", "#227A45"),
(xs[2], y1, "2003 合成 Excel 图片/扩展名导出\n读取 Form 第一项 dataimg[0]\nExcelWebCall.ExcelFile(jsonData)\n响应:二进制下载", "#EAF7EF", "#227A45"),
(xs[3], y1, "2004 数据库文件下载\nDataLink.ExePROCEDURE_Type2004\n返回 bytes / fileName / extension\n响应:二进制下载", "#EAF7EF", "#227A45"),
(xs[0], y2, "15 文件上传\n读取 Request.Files[0]\n拆分 name / suffix / bytes\nDataLink.ExePROCEDURE_Type15\n响应JSON", "#EEF2FF", "#4F46E5"),
(xs[1], y2, "16 数据库文件下载\nDataLink.ExePROCEDURE_Type16\n返回 fileName / suffix / bytes\nbytes 为 null 时直接 return", "#EAF7EF", "#227A45"),
(xs[2], y2, "4000 IP 查询\n读取 ServerVariables / UserHostAddress\n响应:文本 IP", "#FFF7E6", "#B27700"),
(xs[3], y2, "default 通用业务调用\nDataLink.SqlWebCall(type,jsonData,dataobj)\n进入数据库/存储过程二次分发", "#F3E8FF", "#7E22CE"),
]
for cx, cy, text, fill, outline in cards:
draw_box(cx, cy, card_w, card_h, text, fill, outline, font=font_box_small, radius=28)
return panel_x, panel_y, panel_w, panel_h, xs, y2, card_h
def draw_detail_panels(panel_info):
panel_x, panel_y, panel_w, panel_h, xs, y2, card_h = panel_info
resp_x, resp_y, resp_w, resp_h = 260, 2740, 2050, 740
detail_x, detail_y, detail_w, detail_h = 2520, 2740, 2420, 740
draw_panel(resp_x, resp_y, resp_w, resp_h, "响应输出", fill="#FFFFFF", outline="#C7D2E5")
resp_text = (
"文件分支 2001/2002/2003/2004/16\n"
" application/octet-stream + Content-Disposition\n"
" BinaryWrite(bytes) + Flush/End\n\n"
"Type 15 与 default\n"
" Response.Write(JSON 文本)\n\n"
"Type 4000\n"
" Response.Write(userIP)\n\n"
"外层 catch异常时写当前 responseText初始值为 NULL"
)
draw_multiline_left(resp_text, resp_x + 60, resp_y + 115, resp_w - 120, font_note, fill="#344054", line_gap=12)
draw_panel(detail_x, detail_y, detail_w, detail_h, "DataLink.SqlWebCall 默认分支", fill="#FFFFFF", outline="#C7D2E5")
detail_text = (
"初始化InitSystemReg 读取 Web.config 的 ConnectionString\n\n"
"8888 / 5001 / 5002加密、注册/改密、登录并生成 token\n\n"
"1 / 2 / 5旧协议存储过程Param 为拼接字符串\n\n"
"11 / 111 / 12 / 13 / 21新协议存储过程Param 为 JSON 数组字符串\n\n"
"1001 / 1002 / 3 / 4 / 7 / 22 / 3001直接 SQL、建表导入或 SQL 命令执行\n\n"
"输出:数组 JSON 或 { code, message, data }"
)
draw_multiline_left(detail_text, detail_x + 60, detail_y + 115, detail_w - 120, font_note, fill="#344054", line_gap=12)
arrow([(xs[3], y2 + card_h / 2), (xs[3], 2630), (detail_x + detail_w / 2, 2630), (detail_x + detail_w / 2, detail_y)], color="#7E22CE")
arrow([(panel_x + 1150, panel_y + panel_h), (panel_x + 1150, resp_y)], color="#227A45")
arrow([(detail_x, detail_y + detail_h / 2), (resp_x + resp_w, detail_y + detail_h / 2)], color="#7E22CE")
return resp_x, resp_y, resp_w, resp_h
def draw_note_and_end(resp_panel):
resp_x, resp_y, resp_w, resp_h = resp_panel
note_x, note_y, note_w, note_h = 520, 3545, 4160, 150
draw.rounded_rectangle((note_x, note_y, note_x + note_w, note_y + note_h), radius=30, fill="#FFF1F3", outline="#C01048", width=5)
note = "关键注意:入口层缺少统一鉴权和白名单;客户端可通过 Type + Name/Param 触发 SQL 或存储过程能力,异常多数不记录日志。"
draw_multiline_center(note, note_x + note_w / 2, note_y + note_h / 2, note_w - 120, font_note, fill="#7A271A", line_gap=8)
end_x, end_y = W - 560, H - 125
draw_box(end_x, end_y, 420, 95, "请求结束", "#E8F1FF", "#2864B4", font=font_box_small, radius=45)
arrow([(resp_x + resp_w / 2, resp_y + resp_h), (resp_x + resp_w / 2, H - 125), (end_x - 210, H - 125)], color="#344054")
footer = "源文件working/MESCommonBase流程图.mmd PNGworking/MESCommonBase流程图.png"
fw, _ = text_size(footer, font_edge)
draw.text(((W - fw) / 2, H - 45), footer, font=font_edge, fill="#667085")
draw_title()
draw_top_flow()
panel_info = draw_branch_panel()
resp_panel = draw_detail_panels(panel_info)
draw_note_and_end(resp_panel)
img.save(OUT, format="PNG", dpi=(220, 220), optimize=True)
print(OUT)
print(f"{W}x{H}")