diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..697d32a --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Dependencies +node_modules/ +package-lock.json + +# Playwright +test-results/ +playwright-report/ +blob-report/ +.playwright/ + +# Generated output +resume_output.jpg + +# Fonts (downloaded/generated) +fonts/ + +# OS files +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ diff --git a/convert.js b/convert.js new file mode 100644 index 0000000..3093f66 --- /dev/null +++ b/convert.js @@ -0,0 +1,42 @@ +const { chromium } = require('playwright'); +const path = require('path'); + +async function htmlToJpg() { + // Launch browser with custom library path + const browser = await chromium.launch({ + env: { + ...process.env, + LD_LIBRARY_PATH: '/tmp/libs/usr/lib/x86_64-linux-gnu:' + (process.env.LD_LIBRARY_PATH || '') + } + }); + const page = await browser.newPage(); + + // Set viewport to A4 size (210mm x 297mm at 96dpi) + await page.setViewportSize({ width: 794, height: 1123 }); + + // Load the HTML file + const htmlPath = path.join(__dirname, 'resume.html'); + await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle' }); + + // Get the actual page height + const bodyHeight = await page.evaluate(() => document.body.scrollHeight); + + // Set viewport to full page height + await page.setViewportSize({ width: 794, height: bodyHeight }); + + // Take a full-page screenshot + const outputPath = path.join(__dirname, 'resume_output.jpg'); + await page.screenshot({ + path: outputPath, + fullPage: true, + type: 'jpeg', + quality: 95, + }); + + console.log(`Screenshot saved to: ${outputPath}`); + console.log(`Page dimensions: 794 x ${bodyHeight}`); + + await browser.close(); +} + +htmlToJpg().catch(console.error); diff --git a/convert.py b/convert.py new file mode 100644 index 0000000..e891afd --- /dev/null +++ b/convert.py @@ -0,0 +1,63 @@ +import subprocess +import sys + +# Install required packages +def install_packages(): + packages = ['imgkit', 'Pillow'] + for package in packages: + try: + __import__(package.lower().replace('-', '_')) + except ImportError: + print(f"Installing {package}...") + subprocess.check_call([sys.executable, "-m", "pip", "install", package]) + +install_packages() + +import imgkit +from PIL import Image +import os + +def html_to_jpg(html_file, output_file, width=2478): + """Convert HTML file to high-quality JPG""" + + # First, check if wkhtmltopdf/wkhtmltoimage is installed + try: + subprocess.run(['wkhtmltoimage', '--version'], capture_output=True, check=True) + except FileNotFoundError: + print("wkhtmltoimage not found. Please install it:") + print("sudo apt-get install wkhtmltopdf") + print("Or download from: https://wkhtmltopdf.org/downloads.html") + return False + + # Convert HTML to JPG using imgkit + options = { + 'format': 'jpg', + 'quality': 100, + 'width': width, + 'enable-local-file-access': '', + 'encoding': 'UTF-8', + 'no-stop-slow-scripts': '', + } + + try: + imgkit.from_file(html_file, output_file, options=options) + print(f"Successfully converted {html_file} to {output_file}") + + # Verify the output + with Image.open(output_file) as img: + print(f"Output image size: {img.size}") + print(f"Output image mode: {img.mode}") + + return True + except Exception as e: + print(f"Error converting HTML to JPG: {e}") + return False + +if __name__ == "__main__": + html_file = "/home/wonder/code/resume/resume.html" + output_file = "/home/wonder/code/resume/resume_output.jpg" + + if html_to_jpg(html_file, output_file): + print(f"\nOutput saved to: {output_file}") + else: + print("Conversion failed!") diff --git a/download_font.js b/download_font.js new file mode 100644 index 0000000..40314fb --- /dev/null +++ b/download_font.js @@ -0,0 +1,38 @@ +const https = require('https'); +const fs = require('fs'); +const path = require('path'); + +function downloadFile(url, dest) { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(dest); + https.get(url, (response) => { + response.pipe(file); + file.on('finish', () => { + file.close(); + resolve(); + }); + }).on('error', (err) => { + fs.unlink(dest, () => {}); + reject(err); + }); + }); +} + +async function downloadFonts() { + const fontsDir = path.join(__dirname, 'fonts'); + + // Download Noto Sans SC Regular + const regularUrl = 'https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYxNbPzS5HE.ttf'; + const boldUrl = 'https://fonts.gstatic.com/s/notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_EnYxNbPzS5HE.ttf'; + + try { + await downloadFile(regularUrl, path.join(fontsDir, 'NotoSansSC-Regular.ttf')); + console.log('Downloaded NotoSansSC-Regular.ttf'); + await downloadFile(boldUrl, path.join(fontsDir, 'NotoSansSC-Bold.ttf')); + console.log('Downloaded NotoSansSC-Bold.ttf'); + } catch (err) { + console.error('Error downloading fonts:', err); + } +} + +downloadFonts(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..65bd4d5 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "playwright": "^1.61.1", + "playwright-core": "^1.61.1" + } +} diff --git a/resume.html b/resume.html new file mode 100644 index 0000000..5748054 --- /dev/null +++ b/resume.html @@ -0,0 +1,433 @@ + + + + + + 何朝晖 - 简历 + + + +
+ +
+
何朝晖
+
+
+
+ + 15671623703 +
+
+ + hezhaohui0807@163.com +
+
+
+ + 全栈开发(Go/Java后端) +
+
+ + http://47.121.181.112:8080/resume/ +
+
+
+ + +
+
教育经历
+
+
+ 武汉科技大学 + 计算机科学与技术(国家级一流专业) + 本科 +
+
2023-09 ~ 2027-06
+
+ +
+ + +
+
荣誉奖项
+
+
+ "金山杯"华中地区高校第十八届程序设计邀请赛 三等奖 + 证书 +
+
2024-04
+
+
+
+ 国际大学生程序设计竞赛(ICPC)湖北省赛 优秀奖 + 证书 +
+
2024-06
+
+
+ + +
+
实习经历
+
+
国家网络安全人才与创新基地 | 武汉金银湖实验室
+
2025-11 ~ 2026-03
+
+
平台技术部 后端开发 | 华中科技大学-网络空间安全学院
+ + +
+
武汉市知无涯教育科技有限公司
+
2025-06 ~ 2025-08
+
+ +
+ + +
+
项目经历
+
+
Gen2D 游戏素材生成工具
+
2026-05 ~ 至今
+
+
+ 全栈开发 【七牛云 XEngineer 开源项目】 + https://gitee.com/hezhaohui123/gen2d +
+
+ 项目介绍:
+ 基于 Go(Gin) + Eino Agent Graph + GORM + 七牛云 Kodo + React + Zustand 的 AI 2D 游戏素材生成平台。
+ 用户输入文本提示词,自动生成风格一致的 Sprite、背景、UI 元素与动画帧,可直接导入 Unity 等主流 2D 游戏引擎。 +
+
主要工作:
+
    +
  1. 基于 Eino Graph 构建四阶段 DAG 管线,编排 Prompt 优化、素材生成、质量校验、格式适配四节点有序执行。
  2. +
  3. 为提升生成质量稳定性,引入 Reflect 自省机制,自动质检不通过时将拒绝理由回注提示词,同时支持注入用户反馈。
  4. +
  5. 在限流方案的选择上,对比计数器、漏桶、窗口等多种限流机制,选用 go-redis 实现分布式的令牌桶算法限流。
  6. +
  7. 针对 LLM 请求耗时长特性,设计自定义 IO 密集型协程池和 RabbitMQ 消息队列,实现模型调用并发化和异步化。
  8. +
+ +
+
ThumbUP 高并发点赞系统
+
2025-10 ~ 2025-11
+
+
+ 后端开发 【哔哩哔哩技术团队文章复现】 + http://47.121.181.112:3000/wonder/thumb-up +
+
+ 项目介绍:
+ 基于 Spring Boot 3 + Redis + Caffeine + HeavyKeeper + MyBatis-Plus 的高并发点赞系统。
+ 采用 Write-Behind 异步写回架构,写入由 Redis 承接后定时批量落库;读取走 Caffeine、Redis 二级缓存加速。 +
+
主要工作:
+
    +
  1. 基于 Caffeine + Redis 构建二级缓存,热点 Key 提升至本地缓存,避免冷数据污染,降低 Redis 访问压力。
  2. +
  3. Lua 脚本保证点赞原子性,10 秒时间片分桶暂存增量,定时任务批量落库,补偿任务每日兜底,保障最终一致性。
  4. +
  5. 复现HeavyKeeper算法实现 Top-K 热点探测,最小堆维护热点集,概率衰减淘汰冷 Key,周期 fading 适配流量变化。
  6. +
  7. 利用字符串常量池特性,以业务前缀+用户ID为锁对象,实现同用户串行,不同用户并行,减少锁竞争,提升吞吐量。
  8. +
+
+ + +
+
专业技能
+ +
+
+ + diff --git a/target.jpg b/target.jpg new file mode 100644 index 0000000..38a1734 Binary files /dev/null and b/target.jpg differ