from pathlib import Path import json, html, re, hashlib from docx import Document from docx.shared import Inches, Pt, RGBColor from docx.oxml.ns import qn root = Path(__file__).resolve().parents[2] docs = root / 'docs/ranking_v2' assets = root / 'assets/ranking_v2' # Preserve original source inventory and add a clearly separate current file audit. original = json.loads((docs/'art-manifest.json').read_text(encoding='utf8')) if isinstance(original, dict): original = original['originalSources'] texts = '\n'.join(p.read_text(encoding='utf8') for p in assets.rglob('*') if p.suffix in ('.ts', '.prefab', '.plist', '.json') and 'tools' not in p.parts) inventory = [] for folder in ('img', 'reward'): for p in sorted((assets/folder).rglob('*')): if not p.is_file() or p.suffix == '.meta': continue meta = Path(str(p)+'.meta') uuids = re.findall(r'"uuid"\s*:\s*"([^"]+)"', meta.read_text(encoding='utf8')) if meta.exists() else [] referenced = any(u in texts for u in uuids) or ("'"+p.stem+"'") in texts status = '已知当前不使用,保留文件' if p.name == 'reward-coin.png' else '检测到静态引用' if referenced else '需结合动态加载复核,不据此判定闲置' inventory.append(dict(path=p.relative_to(root).as_posix(), bytes=p.stat().st_size, status=status)) manifest = dict(updated='2026-10-28', note='原美术使用标记为历史记录;当前资源扫描不覆盖所有动态选择。', originalSources=original, currentAssets=inventory) (docs/'art-manifest.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n',encoding='utf8') def table(headers, rows): return ''+''.join('' for x in headers)+''+''.join(''+''.join('' for x in row)+'' for row in rows)+'
'+html.escape(x)+'
'+html.escape(str(x))+'
' report = '排行榜素材使用清单

排行榜素材使用清单

更新:2026-10-28。清单位于公共 docs,不参与运行。静态未命中不等于未使用,动态加载须人工复核。

当前说明

宝箱说明及战令领取效果使用本包独立副本,位于 img/reward 和 reward。reward-coin.png 已不用于当前领奖展示。背景以当前 img 文件为准,含 JPG;原效果 JPG 不入运行包。素材未在本轮删除。

原美术来源记录

以下为原始导入记录,包含效果图、标注及系统文件未使用原因;不等于当前完整运行资源列表。

' report += table(['来源','原导入状态','说明'], [(i['source'],'导入使用' if i['used'] else '未用于运行', i['reason']) for i in original]) report += '

当前资源文件盘点

共 '+str(len(inventory))+' 个文件。大小为源文件字节数,不是微信上传包或 ZIP 大小。

' report += table(['项目相对路径','字节','核对状态'],[(i['path'],i['bytes'],i['status']) for i in inventory]) (docs/'素材使用清单.html').write_text(report+'',encoding='utf8') doc=Document() sec=doc.sections[0] sec.page_width=Inches(8.27);sec.page_height=Inches(11.69) sec.top_margin=sec.bottom_margin=Inches(.7) sec.left_margin=sec.right_margin=Inches(.8) for name in ['Normal','Title','Heading 1','Heading 2']: style=doc.styles[name];style.font.name='Microsoft YaHei';style.font.color.rgb=RGBColor(0,0,0) style._element.get_or_add_rPr().rFonts.set(qn('w:eastAsia'),'Microsoft YaHei') doc.styles['Normal'].font.size=Pt(10.5) doc.styles['Normal'].paragraph_format.space_after=Pt(7) doc.styles['Title'].font.size=Pt(22) doc.styles['Heading 1'].font.size=Pt(15) for style in doc.styles: for border in list(style.element.iter(qn('w:pBdr'))): border.getparent().remove(border) for line in (docs/'README.md').read_text(encoding='utf8').splitlines(): if line.startswith('# '): doc.add_paragraph(line[2:],'Title') elif line.startswith('## '): doc.add_paragraph(line[3:],'Heading 1') elif line.strip(): doc.add_paragraph(line) sec.footer.paragraphs[0].text='新排行榜与周竞赛 | 更新于 2026-10-28' doc.save(docs/'排行榜升级后端对接需求.docx') print('Updated DOCX and inventory:',len(inventory),'resource files')