f
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// ProcessFLXG0V4BRequest FLXG0V4B API处理方法
|
||||
// ProcessFLXG0V4BRequest FLXG0V4B API处理方法(身份证排空入口,身份证身份证身份证身份证身份证)
|
||||
func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.FLXG0V4BReq
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998"{
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXG7E8FRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550"|| paramsDto.IDCard == "320682198910134998" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
# 📋 PDF表格转换日志查看指南
|
||||
|
||||
## 📍 日志文件位置
|
||||
|
||||
### 1. 开发环境
|
||||
日志文件存储在项目根目录的 `logs/` 目录下:
|
||||
|
||||
```
|
||||
tyapi-server/
|
||||
└── logs/
|
||||
├── 2024-12-02/ # 按日期分包(如果启用)
|
||||
│ ├── debug.log # Debug级别日志(包含JSON转换详情)
|
||||
│ ├── info.log # Info级别日志(包含转换流程)
|
||||
│ └── error.log # Error级别日志(包含错误信息)
|
||||
└── app.log # 传统模式(如果未启用按日分包)
|
||||
```
|
||||
|
||||
### 2. 生产环境(Docker)
|
||||
日志文件存储在容器的 `/app/logs/` 目录,映射到宿主机的 `./logs/` 目录:
|
||||
|
||||
```bash
|
||||
# 查看宿主机日志
|
||||
./logs/2024-12-02/info.log
|
||||
./logs/2024-12-02/debug.log
|
||||
```
|
||||
|
||||
## 🔍 如何查看转换日志
|
||||
|
||||
### 方法1:实时查看日志(推荐)
|
||||
|
||||
```bash
|
||||
# 查看Info级别日志(转换流程)
|
||||
tail -f logs/2024-12-02/info.log | grep "表格\|JSON\|markdown"
|
||||
|
||||
# 查看Debug级别日志(详细JSON数据)
|
||||
tail -f logs/2024-12-02/debug.log | grep "JSON\|表格"
|
||||
|
||||
# 查看所有PDF相关日志
|
||||
tail -f logs/2024-12-02/*.log | grep -i "pdf\|table\|json"
|
||||
```
|
||||
|
||||
### 方法2:使用Docker查看
|
||||
|
||||
```bash
|
||||
# 查看容器实时日志
|
||||
docker logs -f tyapi-app-prod | grep -i "表格\|json\|markdown"
|
||||
|
||||
# 查看最近的100行日志
|
||||
docker logs --tail 100 tyapi-app-prod | grep -i "表格\|json"
|
||||
```
|
||||
|
||||
### 方法3:搜索特定字段类型
|
||||
|
||||
```bash
|
||||
# 查看请求参数的转换日志
|
||||
grep "request_params" logs/2024-12-02/info.log
|
||||
|
||||
# 查看响应字段的转换日志
|
||||
grep "response_fields" logs/2024-12-02/info.log
|
||||
|
||||
# 查看错误代码的转换日志
|
||||
grep "error_codes" logs/2024-12-02/info.log
|
||||
```
|
||||
|
||||
## 📊 日志级别说明
|
||||
|
||||
### Info级别日志(info.log)
|
||||
包含转换流程的关键步骤:
|
||||
- ✅ 数据格式检测(JSON/Markdown)
|
||||
- ✅ Markdown表格解析开始
|
||||
- ✅ 表格解析成功(表头数量、行数)
|
||||
- ✅ JSON转换完成
|
||||
|
||||
**示例日志:**
|
||||
```
|
||||
2024-12-02T10:30:15Z INFO 开始解析markdown表格并转换为JSON {"field_type": "request_params", "content_length": 1234}
|
||||
2024-12-02T10:30:15Z INFO markdown表格解析成功 {"field_type": "request_params", "header_count": 3, "row_count": 5}
|
||||
2024-12-02T10:30:15Z INFO 表格数据已转换为JSON格式 {"field_type": "request_params", "json_array_length": 5}
|
||||
```
|
||||
|
||||
### Debug级别日志(debug.log)
|
||||
包含详细的转换数据:
|
||||
- 🔍 原始内容预览
|
||||
- 🔍 解析后的表头列表
|
||||
- 🔍 转换后的完整JSON数据(前1000字符)
|
||||
- 🔍 每行的转换详情
|
||||
|
||||
**示例日志:**
|
||||
```
|
||||
2024-12-02T10:30:15Z DEBUG 转换后的JSON数据预览 {"field_type": "request_params", "json_length": 2345, "json_preview": "[{\"字段名\":\"name\",\"类型\":\"string\",\"说明\":\"姓名\"}...]"}
|
||||
```
|
||||
|
||||
### Error级别日志(error.log)
|
||||
包含转换过程中的错误:
|
||||
- ❌ Markdown解析失败
|
||||
- ❌ JSON序列化失败
|
||||
- ❌ 数据格式错误
|
||||
|
||||
**示例日志:**
|
||||
```
|
||||
2024-12-02T10:30:15Z ERROR 解析markdown表格失败 {"field_type": "request_params", "error": "无法解析表格:未找到表头", "content_preview": "..."}
|
||||
```
|
||||
|
||||
## 🔎 日志关键词搜索
|
||||
|
||||
### 转换流程关键词
|
||||
- `开始解析markdown表格` - 转换开始
|
||||
- `markdown表格解析成功` - 解析完成
|
||||
- `表格数据已转换为JSON格式` - JSON转换完成
|
||||
- `转换后的JSON数据预览` - JSON数据详情
|
||||
|
||||
### 数据格式关键词
|
||||
- `数据已经是JSON格式` - 数据源是JSON
|
||||
- `从JSON对象中提取数组数据` - 从JSON对象提取
|
||||
- `解析markdown表格并转换为JSON` - Markdown转JSON
|
||||
|
||||
### 错误关键词
|
||||
- `解析markdown表格失败` - 解析错误
|
||||
- `JSON序列化失败` - JSON错误
|
||||
- `字段内容为空` - 空数据
|
||||
|
||||
## 📝 日志配置
|
||||
|
||||
确保日志级别设置为 `debug` 才能看到详细的JSON转换日志:
|
||||
|
||||
```yaml
|
||||
# config.yaml 或 configs/env.development.yaml
|
||||
logger:
|
||||
level: "debug" # 开发环境使用debug级别
|
||||
format: "console" # 或 "json"
|
||||
output: "file" # 输出到文件
|
||||
log_dir: "logs" # 日志目录
|
||||
use_daily: true # 启用按日分包
|
||||
```
|
||||
|
||||
## 🛠️ 常用命令
|
||||
|
||||
```bash
|
||||
# 查看今天的Info日志
|
||||
cat logs/$(date +%Y-%m-%d)/info.log | grep "表格\|JSON"
|
||||
|
||||
# 查看最近的转换日志(最后50行)
|
||||
tail -n 50 logs/$(date +%Y-%m-%d)/info.log
|
||||
|
||||
# 搜索特定产品的转换日志
|
||||
grep "product_id.*xxx" logs/$(date +%Y-%m-%d)/info.log
|
||||
|
||||
# 查看所有错误
|
||||
grep "ERROR" logs/$(date +%Y-%m-%d)/error.log
|
||||
|
||||
# 统计转换次数
|
||||
grep "表格数据已转换为JSON格式" logs/$(date +%Y-%m-%d)/info.log | wc -l
|
||||
```
|
||||
|
||||
## 💡 调试技巧
|
||||
|
||||
1. **查看完整JSON数据**:如果JSON数据超过1000字符,查看debug.log获取完整内容
|
||||
2. **追踪转换流程**:使用 `field_type` 字段过滤特定字段的转换日志
|
||||
3. **定位错误**:查看error.log中的 `content_preview` 字段了解原始数据
|
||||
4. **性能监控**:统计转换次数和耗时,优化转换逻辑
|
||||
|
||||
## 📌 注意事项
|
||||
|
||||
- Debug级别日志可能包含大量数据,注意日志文件大小
|
||||
- 生产环境建议使用 `info` 级别,减少日志量
|
||||
- JSON预览限制在1000字符,完整数据请查看debug日志
|
||||
- 日志文件按日期自动分包,便于管理和查找
|
||||
|
||||
@@ -87,6 +87,7 @@ func (r *DatabaseTableRenderer) RenderTable(pdf *gofpdf.Fpdf, tableData *TableDa
|
||||
if currentY+estimatedHeaderHeight > pageHeight-bottomMargin {
|
||||
r.logger.Debug("表头前需要分页", zap.Float64("current_y", currentY))
|
||||
pdf.AddPage()
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// 绘制表头
|
||||
@@ -448,6 +449,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
|
||||
zap.Float64("current_y", currentY),
|
||||
zap.Float64("page_height", pageHeight))
|
||||
pdf.AddPage()
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
// 在新页面上重新绘制表头
|
||||
if len(headers) > 0 && len(headerColWidths) > 0 {
|
||||
newHeaderStartY := pdf.GetY()
|
||||
@@ -523,6 +525,7 @@ func (r *DatabaseTableRenderer) renderRows(pdf *gofpdf.Fpdf, rows [][]string, co
|
||||
zap.Int("row_index", rowIndex),
|
||||
zap.Float64("row_height", maxCellHeight))
|
||||
pdf.AddPage()
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
startY = pdf.GetY()
|
||||
}
|
||||
|
||||
|
||||
@@ -205,8 +205,10 @@ func (fm *FontManager) getChineseFontPaths() []string {
|
||||
func (fm *FontManager) getWatermarkFontPaths() []string {
|
||||
// 水印字体文件名(尝试大小写变体)
|
||||
fontNames := []string{
|
||||
"YunFengFeiYunTi-2.ttf", // 优先尝试大写版本
|
||||
"yunfengfeiyunti-2.ttf", // 小写版本(兼容)
|
||||
// "XuanZongTi-v0.1.otf", //玄宗字体不支持otf
|
||||
"WenYuanSerifSC-Bold.ttf", //文渊雅黑
|
||||
// "YunFengFeiYunTi-2.ttf", // 毛笔字体
|
||||
// "yunfengfeiyunti-2.ttf", // 毛笔字体小写版本(兼容)
|
||||
}
|
||||
|
||||
return fm.buildFontPaths(fontNames)
|
||||
|
||||
@@ -56,109 +56,142 @@ func NewPageBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
// 封面页底部为价格预留的高度(mm),避免价格被挤到单独一页
|
||||
const firstPagePriceReservedHeight = 18.0
|
||||
|
||||
// ContentStartYBelowHeader 页眉(logo+横线)下方的正文起始 Y(mm),表格等 AddPage 后须设为此值,避免与 logo 重叠(留足顶间距)
|
||||
const ContentStartYBelowHeader = 50.0
|
||||
|
||||
// AddFirstPage 添加第一页(封面页 - 产品功能简述)
|
||||
// 页眉与水印由 SetHeaderFunc 在每页 AddPage 时自动绘制,此处不再重复调用
|
||||
// 自动限制描述/详情高度,保证价格与封面同页,不单独成页
|
||||
func (pb *PageBuilder) AddFirstPage(pdf *gofpdf.Fpdf, product *entities.Product, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
|
||||
pdf.AddPage()
|
||||
|
||||
// 添加页眉(logo和文字)
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
_, _, _, bottomMargin := pdf.GetMargins()
|
||||
// 内容区最大 Y:超出则不再绘制,留出底部给价格,避免价格单独一页
|
||||
maxContentY := pageHeight - bottomMargin - firstPagePriceReservedHeight
|
||||
|
||||
// 添加水印
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
|
||||
// 封面页布局 - 居中显示
|
||||
pageWidth, _ := pdf.GetPageSize()
|
||||
|
||||
// 标题区域(页面中上部)
|
||||
pdf.SetY(80)
|
||||
// 标题区域(在页眉下方留足间距,避免与 logo 重叠)
|
||||
pdf.SetY(ContentStartYBelowHeader + 6)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "B", 32)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
|
||||
// 清理产品名称中的无效字符
|
||||
cleanName := pb.textProcessor.CleanText(product.Name)
|
||||
pdf.CellFormat(0, lineHt*1.5, cleanName, "", 1, "C", false, 0, "")
|
||||
|
||||
// 添加"接口文档"副标题
|
||||
pdf.Ln(10)
|
||||
pdf.Ln(6)
|
||||
pb.fontManager.SetFont(pdf, "", 18)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "接口文档", "", 1, "C", false, 0, "")
|
||||
|
||||
// 分隔线
|
||||
pdf.Ln(20)
|
||||
pdf.SetLineWidth(0.5)
|
||||
pdf.Line(pageWidth*0.2, pdf.GetY(), pageWidth*0.8, pdf.GetY())
|
||||
|
||||
// 产品编码(居中)
|
||||
pdf.Ln(30)
|
||||
// 产品编码
|
||||
pdf.Ln(16)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "", 14)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, fmt.Sprintf("产品编码:%s", product.Code), "", 1, "C", false, 0, "")
|
||||
|
||||
// 产品描述(居中显示,段落格式)
|
||||
pdf.Ln(12)
|
||||
pdf.SetLineWidth(0.5)
|
||||
pdf.Line(pageWidth*0.2, pdf.GetY(), pageWidth*0.8, pdf.GetY())
|
||||
|
||||
// 产品描述(居中,无单独标题)
|
||||
if product.Description != "" {
|
||||
pdf.Ln(25)
|
||||
desc := pb.textProcessor.StripHTML(product.Description)
|
||||
pdf.Ln(10)
|
||||
desc := pb.textProcessor.HTMLToPlainWithBreaks(product.Description)
|
||||
desc = pb.textProcessor.CleanText(desc)
|
||||
pb.fontManager.SetFont(pdf, "", 14)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
// 居中对齐的MultiCell(通过计算宽度实现)
|
||||
descWidth := pageWidth * 0.7
|
||||
descLines := pb.safeSplitText(pdf, desc, descWidth, chineseFontAvailable)
|
||||
currentX := (pageWidth - descWidth) / 2
|
||||
for _, line := range descLines {
|
||||
pdf.SetX(currentX)
|
||||
pdf.CellFormat(descWidth, lineHt*1.5, line, "", 1, "C", false, 0, "")
|
||||
}
|
||||
pb.drawRichTextBlock(pdf, desc, pageWidth*0.7, lineHt*1.5, maxContentY, "C", true, chineseFontAvailable)
|
||||
}
|
||||
|
||||
// 产品详情(如果存在)
|
||||
if product.Content != "" {
|
||||
pdf.Ln(20)
|
||||
content := pb.textProcessor.StripHTML(product.Content)
|
||||
content = pb.textProcessor.CleanText(content)
|
||||
pb.fontManager.SetFont(pdf, "", 12)
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
contentWidth := pageWidth * 0.7
|
||||
contentLines := pb.safeSplitText(pdf, content, contentWidth, chineseFontAvailable)
|
||||
currentX := (pageWidth - contentWidth) / 2
|
||||
for _, line := range contentLines {
|
||||
pdf.SetX(currentX)
|
||||
pdf.CellFormat(contentWidth, lineHt*1.4, line, "", 1, "C", false, 0, "")
|
||||
}
|
||||
}
|
||||
|
||||
// 价格信息(右下角,在产品详情之后)
|
||||
// 产品详情已移至单独一页,见 AddProductContentPage
|
||||
if !product.Price.IsZero() {
|
||||
// 获取产品详情结束后的Y坐标,稍微下移显示价格
|
||||
contentEndY := pdf.GetY()
|
||||
pdf.SetY(contentEndY + 5)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "", 14)
|
||||
_, priceLineHt := pdf.GetFontSize()
|
||||
reservedZoneY := pageHeight - bottomMargin - firstPagePriceReservedHeight + 6
|
||||
priceY := reservedZoneY
|
||||
if pdf.GetY()+5 > reservedZoneY {
|
||||
priceY = pdf.GetY() + 5
|
||||
}
|
||||
pdf.SetY(priceY)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
priceText := fmt.Sprintf("价格:%s 元", product.Price.String())
|
||||
textWidth := pdf.GetStringWidth(priceText)
|
||||
// 右对齐:从页面宽度减去文本宽度和右边距(15mm)
|
||||
pdf.SetX(pageWidth - textWidth - 15)
|
||||
pdf.CellFormat(textWidth, priceLineHt, priceText, "", 0, "R", false, 0, "")
|
||||
}
|
||||
}
|
||||
|
||||
// AddProductContentPage 添加产品详情页(另起一页,左对齐,段前两空格)
|
||||
func (pb *PageBuilder) AddProductContentPage(pdf *gofpdf.Fpdf, product *entities.Product, chineseFontAvailable bool) {
|
||||
if product.Content == "" {
|
||||
return
|
||||
}
|
||||
pdf.AddPage()
|
||||
pageWidth, _ := pdf.GetPageSize()
|
||||
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "B", 14)
|
||||
_, titleHt := pdf.GetFontSize()
|
||||
pdf.CellFormat(0, titleHt, "产品详情", "", 1, "L", false, 0, "")
|
||||
pdf.Ln(6)
|
||||
content := pb.textProcessor.HTMLToPlainWithBreaks(product.Content)
|
||||
content = pb.textProcessor.CleanText(content)
|
||||
pb.fontManager.SetFont(pdf, "", 12)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
// 产品详情页不做“略写”截断,全部内容都渲染出来,允许 gofpdf 自动分页
|
||||
pb.drawRichTextBlockNoLimit(pdf, content, pageWidth*0.9, lineHt*1.4, "L", true, chineseFontAvailable)
|
||||
}
|
||||
|
||||
// drawRichTextBlockNoLimit 渲染富文本块,不根据 maxContentY 截断,允许自动分页,适合“产品详情”等必须全部展示的内容
|
||||
func (pb *PageBuilder) drawRichTextBlockNoLimit(pdf *gofpdf.Fpdf, text string, contentWidth, lineHeight float64, align string, firstLineIndent bool, chineseFontAvailable bool) {
|
||||
pageWidth, _ := pdf.GetPageSize()
|
||||
leftMargin, _, _, _ := pdf.GetMargins()
|
||||
currentX := (pageWidth - contentWidth) / 2
|
||||
if align == "L" {
|
||||
currentX = leftMargin
|
||||
}
|
||||
paragraphs := strings.Split(text, "\n\n")
|
||||
for pIdx, para := range paragraphs {
|
||||
para = strings.TrimSpace(para)
|
||||
if para == "" {
|
||||
continue
|
||||
}
|
||||
if pIdx > 0 {
|
||||
pdf.Ln(4)
|
||||
}
|
||||
firstLineOfPara := true
|
||||
lines := strings.Split(para, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
wrapped := pb.safeSplitText(pdf, line, contentWidth, chineseFontAvailable)
|
||||
for _, w := range wrapped {
|
||||
x := currentX
|
||||
if align == "L" && firstLineIndent && firstLineOfPara {
|
||||
x = leftMargin + paragraphIndentMM
|
||||
}
|
||||
pdf.SetX(x)
|
||||
pdf.CellFormat(contentWidth, lineHeight, w, "", 1, align, false, 0, "")
|
||||
firstLineOfPara = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddDocumentationPages 添加接口文档页面
|
||||
// 每页的页眉与水印由 SetHeaderFunc 在 AddPage 时自动绘制
|
||||
func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
|
||||
// 创建自定义的AddPage函数,确保每页都有水印
|
||||
addPageWithWatermark := func() {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable) // 每页都添加水印
|
||||
}
|
||||
|
||||
addPageWithWatermark()
|
||||
|
||||
pdf.SetY(45)
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
pb.fontManager.SetFont(pdf, "B", 18)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "接口文档", "", 1, "L", false, 0, "")
|
||||
@@ -212,10 +245,7 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
|
||||
pdf.SetTextColor(0, 0, 0) // 确保深黑色
|
||||
pb.fontManager.SetFont(pdf, "B", 14)
|
||||
pdf.CellFormat(0, lineHt, "请求示例:", "", 1, "L", false, 0, "")
|
||||
// JSON中可能包含中文值,使用黑体字体
|
||||
pb.fontManager.SetFont(pdf, "", 9) // 使用黑体显示JSON(支持中文)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pdf.MultiCell(0, lineHt*1.3, jsonExample, "", "L", false)
|
||||
pb.drawJSONInCenteredTable(pdf, jsonExample, lineHt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +257,7 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "响应示例:", "", 1, "L", false, 0, "")
|
||||
|
||||
// 优先尝试提取和格式化JSON
|
||||
// 优先尝试提取和格式化JSON(表格包裹,居中,内容左对齐)
|
||||
jsonContent := pb.jsonProcessor.ExtractJSON(doc.ResponseExample)
|
||||
if jsonContent != "" {
|
||||
// 格式化JSON
|
||||
@@ -235,9 +265,7 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
|
||||
if err == nil {
|
||||
jsonContent = formattedJSON
|
||||
}
|
||||
pdf.SetTextColor(0, 0, 0) // 确保深黑色
|
||||
pb.fontManager.SetFont(pdf, "", 9) // 使用等宽字体显示JSON(支持中文)
|
||||
pdf.MultiCell(0, lineHt*1.3, jsonContent, "", "L", false)
|
||||
pb.drawJSONInCenteredTable(pdf, jsonContent, lineHt)
|
||||
} else {
|
||||
// 如果没有JSON,尝试使用表格方式处理
|
||||
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "response_example"); err != nil {
|
||||
@@ -253,8 +281,9 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
|
||||
}
|
||||
}
|
||||
|
||||
// 返回字段说明
|
||||
// 返回字段说明(确保在页眉下方,避免与 logo 重叠)
|
||||
if doc.ResponseFields != "" {
|
||||
pb.ensureContentBelowHeader(pdf)
|
||||
pdf.Ln(8)
|
||||
// 显示标题
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
@@ -306,18 +335,11 @@ func (pb *PageBuilder) AddDocumentationPages(pdf *gofpdf.Fpdf, doc *entities.Pro
|
||||
}
|
||||
|
||||
// AddDocumentationPagesWithoutAdditionalInfo 添加接口文档页面(不包含二维码和说明)
|
||||
// 用于组合包场景,在所有文档渲染完成后统一添加二维码和说明
|
||||
// 用于组合包场景,在所有文档渲染完成后统一添加二维码和说明。每页页眉与水印由 SetHeaderFunc 自动绘制。
|
||||
func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.ProductDocumentation, chineseFontAvailable bool) {
|
||||
// 创建自定义的AddPage函数,确保每页都有水印
|
||||
addPageWithWatermark := func() {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable) // 每页都添加水印
|
||||
}
|
||||
|
||||
addPageWithWatermark()
|
||||
|
||||
pdf.SetY(45)
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
pb.fontManager.SetFont(pdf, "B", 18)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "接口文档", "", 1, "L", false, 0, "")
|
||||
@@ -371,10 +393,7 @@ func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fp
|
||||
pdf.SetTextColor(0, 0, 0) // 确保深黑色
|
||||
pb.fontManager.SetFont(pdf, "B", 14)
|
||||
pdf.CellFormat(0, lineHt, "请求示例:", "", 1, "L", false, 0, "")
|
||||
// JSON中可能包含中文值,使用黑体字体
|
||||
pb.fontManager.SetFont(pdf, "", 9) // 使用黑体显示JSON(支持中文)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pdf.MultiCell(0, lineHt*1.3, jsonExample, "", "L", false)
|
||||
pb.drawJSONInCenteredTable(pdf, jsonExample, lineHt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +405,7 @@ func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fp
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "响应示例:", "", 1, "L", false, 0, "")
|
||||
|
||||
// 优先尝试提取和格式化JSON
|
||||
// 优先尝试提取和格式化JSON(表格包裹,居中,内容左对齐)
|
||||
jsonContent := pb.jsonProcessor.ExtractJSON(doc.ResponseExample)
|
||||
if jsonContent != "" {
|
||||
// 格式化JSON
|
||||
@@ -394,9 +413,7 @@ func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fp
|
||||
if err == nil {
|
||||
jsonContent = formattedJSON
|
||||
}
|
||||
pdf.SetTextColor(0, 0, 0) // 确保深黑色
|
||||
pb.fontManager.SetFont(pdf, "", 9) // 使用等宽字体显示JSON(支持中文)
|
||||
pdf.MultiCell(0, lineHt*1.3, jsonContent, "", "L", false)
|
||||
pb.drawJSONInCenteredTable(pdf, jsonContent, lineHt)
|
||||
} else {
|
||||
// 如果没有JSON,尝试使用表格方式处理
|
||||
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "response_example"); err != nil {
|
||||
@@ -412,8 +429,9 @@ func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fp
|
||||
}
|
||||
}
|
||||
|
||||
// 返回字段说明
|
||||
// 返回字段说明(确保在页眉下方,避免与 logo 重叠)
|
||||
if doc.ResponseFields != "" {
|
||||
pb.ensureContentBelowHeader(pdf)
|
||||
pdf.Ln(8)
|
||||
// 显示标题
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
@@ -463,17 +481,11 @@ func (pb *PageBuilder) AddDocumentationPagesWithoutAdditionalInfo(pdf *gofpdf.Fp
|
||||
}
|
||||
|
||||
// AddSubProductDocumentationPages 添加子产品的接口文档页面(用于组合包)
|
||||
// 每页页眉与水印由 SetHeaderFunc 在 AddPage 时自动绘制
|
||||
func (pb *PageBuilder) AddSubProductDocumentationPages(pdf *gofpdf.Fpdf, subProduct *entities.Product, doc *entities.ProductDocumentation, chineseFontAvailable bool, isLastSubProduct bool) {
|
||||
// 创建自定义的AddPage函数,确保每页都有水印
|
||||
addPageWithWatermark := func() {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable) // 每页都添加水印
|
||||
}
|
||||
|
||||
addPageWithWatermark()
|
||||
|
||||
pdf.SetY(45)
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
pb.fontManager.SetFont(pdf, "B", 18)
|
||||
_, lineHt := pdf.GetFontSize()
|
||||
|
||||
@@ -533,10 +545,7 @@ func (pb *PageBuilder) AddSubProductDocumentationPages(pdf *gofpdf.Fpdf, subProd
|
||||
pdf.SetTextColor(0, 0, 0) // 确保深黑色
|
||||
pb.fontManager.SetFont(pdf, "B", 14)
|
||||
pdf.CellFormat(0, lineHt, "请求示例:", "", 1, "L", false, 0, "")
|
||||
// JSON中可能包含中文值,使用黑体字体
|
||||
pb.fontManager.SetFont(pdf, "", 9) // 使用黑体显示JSON(支持中文)
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pdf.MultiCell(0, lineHt*1.3, jsonExample, "", "L", false)
|
||||
pb.drawJSONInCenteredTable(pdf, jsonExample, lineHt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +557,7 @@ func (pb *PageBuilder) AddSubProductDocumentationPages(pdf *gofpdf.Fpdf, subProd
|
||||
_, lineHt = pdf.GetFontSize()
|
||||
pdf.CellFormat(0, lineHt, "响应示例:", "", 1, "L", false, 0, "")
|
||||
|
||||
// 优先尝试提取和格式化JSON
|
||||
// 优先尝试提取和格式化JSON(表格包裹,居中,内容左对齐)
|
||||
jsonContent := pb.jsonProcessor.ExtractJSON(doc.ResponseExample)
|
||||
if jsonContent != "" {
|
||||
// 格式化JSON
|
||||
@@ -556,9 +565,7 @@ func (pb *PageBuilder) AddSubProductDocumentationPages(pdf *gofpdf.Fpdf, subProd
|
||||
if err == nil {
|
||||
jsonContent = formattedJSON
|
||||
}
|
||||
pdf.SetTextColor(0, 0, 0) // 确保深黑色
|
||||
pb.fontManager.SetFont(pdf, "", 9) // 使用等宽字体显示JSON(支持中文)
|
||||
pdf.MultiCell(0, lineHt*1.3, jsonContent, "", "L", false)
|
||||
pb.drawJSONInCenteredTable(pdf, jsonContent, lineHt)
|
||||
} else {
|
||||
// 如果没有JSON,尝试使用表格方式处理
|
||||
if err := pb.tableParser.ParseAndRenderTable(context.Background(), pdf, doc, "response_example"); err != nil {
|
||||
@@ -574,8 +581,9 @@ func (pb *PageBuilder) AddSubProductDocumentationPages(pdf *gofpdf.Fpdf, subProd
|
||||
}
|
||||
}
|
||||
|
||||
// 返回字段说明
|
||||
// 返回字段说明(确保在页眉下方,避免与 logo 重叠)
|
||||
if doc.ResponseFields != "" {
|
||||
pb.ensureContentBelowHeader(pdf)
|
||||
pdf.Ln(8)
|
||||
// 显示标题
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
@@ -979,73 +987,121 @@ func (pb *PageBuilder) addHeader(pdf *gofpdf.Fpdf, chineseFontAvailable bool) {
|
||||
|
||||
// 绘制下横线(优化位置,左边距是15mm)
|
||||
pdf.Line(15, 22, 75, 22)
|
||||
|
||||
// 所有自动分页后的正文统一从页眉下方固定位置开始,避免内容顶到 logo 或水印
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// addWatermark 添加水印(从左边开始向上倾斜45度,考虑可用区域)
|
||||
// ensureContentBelowHeader 若当前 Y 在页眉区内则下移到正文区,避免与 logo 重叠
|
||||
func (pb *PageBuilder) ensureContentBelowHeader(pdf *gofpdf.Fpdf) {
|
||||
if pdf.GetY() < ContentStartYBelowHeader {
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
}
|
||||
|
||||
// addWatermark 添加水印:自左下角往右上角倾斜 45°,单条水印居中于页面,样式柔和
|
||||
func (pb *PageBuilder) addWatermark(pdf *gofpdf.Fpdf, chineseFontAvailable bool) {
|
||||
// 如果中文字体不可用,跳过水印(避免显示乱码)
|
||||
if !chineseFontAvailable {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存当前图形状态
|
||||
pdf.TransformBegin()
|
||||
defer pdf.TransformEnd()
|
||||
|
||||
// 获取页面尺寸和边距
|
||||
_, pageHeight := pdf.GetPageSize()
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
leftMargin, topMargin, _, bottomMargin := pdf.GetMargins()
|
||||
|
||||
// 计算实际可用区域高度
|
||||
usableHeight := pageHeight - topMargin - bottomMargin
|
||||
usableWidth := pageWidth - leftMargin*2
|
||||
|
||||
// 设置水印样式(使用水印字体,非黑体)
|
||||
fontSize := 45.0
|
||||
|
||||
fontSize := 42.0
|
||||
pb.fontManager.SetWatermarkFont(pdf, "", fontSize)
|
||||
|
||||
// 设置灰色和透明度(加深水印,使其更明显)
|
||||
pdf.SetTextColor(180, 180, 180) // 深一点的灰色
|
||||
pdf.SetAlpha(0.25, "Normal") // 增加透明度,让水印更明显
|
||||
// 加深水印:更深的灰与更高不透明度,保证可见
|
||||
pdf.SetTextColor(150, 150, 150)
|
||||
pdf.SetAlpha(0.32, "Normal")
|
||||
|
||||
// 计算文字宽度
|
||||
textWidth := pdf.GetStringWidth(pb.watermarkText)
|
||||
if textWidth == 0 {
|
||||
// 如果无法获取宽度(字体未注册),使用估算值(中文字符大约每个 fontSize/3 mm)
|
||||
textWidth = float64(len([]rune(pb.watermarkText))) * fontSize / 3.0
|
||||
}
|
||||
|
||||
// 从左边开始,计算起始位置
|
||||
// 起始X:左边距
|
||||
// 起始Y:考虑水印文字长度和旋转后需要的空间
|
||||
startX := leftMargin
|
||||
startY := topMargin + textWidth*0.5 // 为旋转留出空间
|
||||
|
||||
// 移动到起始位置
|
||||
pdf.TransformTranslate(startX, startY)
|
||||
|
||||
// 向上倾斜45度(顺时针旋转45度,即-45度,或逆时针315度)
|
||||
pdf.TransformRotate(-45, 0, 0)
|
||||
|
||||
// 检查文字是否会超出可用区域(旋转后的对角线长度)
|
||||
// 旋转后对角线长度,用于缩放与定位
|
||||
rotatedDiagonal := math.Sqrt(textWidth*textWidth + fontSize*fontSize)
|
||||
if rotatedDiagonal > usableHeight*0.8 {
|
||||
// 如果太大,缩小字体
|
||||
fontSize = fontSize * usableHeight * 0.8 / rotatedDiagonal
|
||||
if rotatedDiagonal > usableHeight*0.75 {
|
||||
fontSize = fontSize * usableHeight * 0.75 / rotatedDiagonal
|
||||
pb.fontManager.SetWatermarkFont(pdf, "", fontSize)
|
||||
textWidth = pdf.GetStringWidth(pb.watermarkText)
|
||||
if textWidth == 0 {
|
||||
textWidth = float64(len([]rune(pb.watermarkText))) * fontSize / 3.0
|
||||
}
|
||||
rotatedDiagonal = math.Sqrt(textWidth*textWidth + fontSize*fontSize)
|
||||
}
|
||||
|
||||
// 从左边开始绘制水印文字
|
||||
// 自左下角往右上角:起点在可用区域左下角,逆时针旋转 +45°
|
||||
startX := leftMargin
|
||||
startY := pageHeight - bottomMargin
|
||||
|
||||
// 沿 +45° 方向居中:对角线在可用区域内居中
|
||||
diagW := rotatedDiagonal * math.Cos(45*math.Pi/180)
|
||||
offsetX := (usableWidth - diagW) * 0.5
|
||||
startX += offsetX
|
||||
startY -= rotatedDiagonal * 0.5
|
||||
|
||||
pdf.TransformTranslate(startX, startY)
|
||||
pdf.TransformRotate(45, 0, 0)
|
||||
|
||||
pdf.SetXY(0, 0)
|
||||
pdf.CellFormat(textWidth, fontSize, pb.watermarkText, "", 0, "L", false, 0, "")
|
||||
|
||||
// 恢复透明度和颜色
|
||||
pdf.SetAlpha(1.0, "Normal")
|
||||
pdf.SetTextColor(0, 0, 0) // 恢复为黑色
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
}
|
||||
|
||||
// 段前缩进宽度(约两字符,mm)
|
||||
const paragraphIndentMM = 7.0
|
||||
|
||||
// drawRichTextBlock 按段落与换行绘制文本块(还原 HTML 换行),超出 maxContentY 截断并显示 …
|
||||
// align: "C" 居中;"L" 左对齐。firstLineIndent 为 true 时每段首行缩进(段前两空格效果)。
|
||||
func (pb *PageBuilder) drawRichTextBlock(pdf *gofpdf.Fpdf, text string, contentWidth, lineHeight float64, maxContentY float64, align string, firstLineIndent bool, chineseFontAvailable bool) {
|
||||
pageWidth, _ := pdf.GetPageSize()
|
||||
leftMargin, _, _, _ := pdf.GetMargins()
|
||||
currentX := (pageWidth - contentWidth) / 2
|
||||
if align == "L" {
|
||||
currentX = leftMargin
|
||||
}
|
||||
paragraphs := strings.Split(text, "\n\n")
|
||||
for pIdx, para := range paragraphs {
|
||||
para = strings.TrimSpace(para)
|
||||
if para == "" {
|
||||
continue
|
||||
}
|
||||
if pIdx > 0 && pdf.GetY()+lineHeight <= maxContentY {
|
||||
pdf.Ln(4)
|
||||
}
|
||||
firstLineOfPara := true
|
||||
lines := strings.Split(para, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
wrapped := pb.safeSplitText(pdf, line, contentWidth, chineseFontAvailable)
|
||||
for _, w := range wrapped {
|
||||
if pdf.GetY()+lineHeight > maxContentY {
|
||||
pdf.SetX(currentX)
|
||||
pdf.CellFormat(contentWidth, lineHeight, "…", "", 1, align, false, 0, "")
|
||||
return
|
||||
}
|
||||
x := currentX
|
||||
if align == "L" && firstLineIndent && firstLineOfPara {
|
||||
x = leftMargin + paragraphIndentMM
|
||||
}
|
||||
pdf.SetX(x)
|
||||
pdf.CellFormat(contentWidth, lineHeight, w, "", 1, align, false, 0, "")
|
||||
firstLineOfPara = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getContentPreview 获取内容预览(用于日志记录)
|
||||
@@ -1057,6 +1113,49 @@ func (pb *PageBuilder) getContentPreview(content string, maxLen int) string {
|
||||
return content[:maxLen] + "..."
|
||||
}
|
||||
|
||||
// drawJSONInCenteredTable 在居中表格中绘制 JSON 文本(表格居中,内容左对齐);空间不足时自动换页避免压住 logo
|
||||
func (pb *PageBuilder) drawJSONInCenteredTable(pdf *gofpdf.Fpdf, jsonContent string, lineHt float64) {
|
||||
jsonContent = strings.TrimSpace(jsonContent)
|
||||
if jsonContent == "" {
|
||||
return
|
||||
}
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
leftMargin, _, rightMargin, bottomMargin := pdf.GetMargins()
|
||||
usableWidth := pageWidth - leftMargin - rightMargin
|
||||
// 表格宽度取可用宽度的 85%,居中
|
||||
tableWidth := usableWidth * 0.85
|
||||
startX := (pageWidth - tableWidth) / 2
|
||||
padding := 4.0
|
||||
innerWidth := tableWidth - 2*padding
|
||||
lineHeight := lineHt * 1.3
|
||||
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
pb.fontManager.SetFont(pdf, "", 9)
|
||||
lines := pdf.SplitText(jsonContent, innerWidth)
|
||||
totalHeight := float64(len(lines))*lineHeight + 2*padding
|
||||
|
||||
// 当前页剩余高度不足时先换页,避免表格被 logo 或底部裁掉
|
||||
currentY := pdf.GetY()
|
||||
if currentY+totalHeight > pageHeight-bottomMargin {
|
||||
pdf.AddPage()
|
||||
currentY = ContentStartYBelowHeader
|
||||
pdf.SetY(currentY)
|
||||
}
|
||||
|
||||
startY := currentY
|
||||
// 绘制表格边框
|
||||
pdf.SetDrawColor(180, 180, 180)
|
||||
pdf.Rect(startX, startY, tableWidth, totalHeight, "D")
|
||||
pdf.SetDrawColor(0, 0, 0)
|
||||
// 表格内内容左对齐
|
||||
pdf.SetY(startY + padding)
|
||||
for _, line := range lines {
|
||||
pdf.SetX(startX + padding)
|
||||
pdf.CellFormat(innerWidth, lineHeight, line, "", 1, "L", false, 0, "")
|
||||
}
|
||||
pdf.SetY(startY + totalHeight)
|
||||
}
|
||||
|
||||
// safeSplitText 安全地分割文本,避免在没有中文字体时调用SplitText导致panic
|
||||
func (pb *PageBuilder) safeSplitText(pdf *gofpdf.Fpdf, text string, width float64, chineseFontAvailable bool) []string {
|
||||
// 检查文本是否包含中文字符
|
||||
@@ -1167,12 +1266,10 @@ func (pb *PageBuilder) addAdditionalInfo(pdf *gofpdf.Fpdf, doc *entities.Product
|
||||
currentY := pdf.GetY()
|
||||
remainingHeight := pageHeight - currentY - bottomMargin
|
||||
|
||||
// 如果剩余空间不足,添加新页
|
||||
// 如果剩余空间不足,添加新页(页眉与水印由 SetHeaderFunc 自动绘制)
|
||||
if remainingHeight < 100 {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
pdf.SetY(45)
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// 添加分隔线
|
||||
@@ -1327,9 +1424,7 @@ func (pb *PageBuilder) addQRCodeSection(pdf *gofpdf.Fpdf, doc *entities.ProductD
|
||||
// 检查是否需要换页(为二维码预留空间)
|
||||
if pageHeight-currentY-bottomMargin < 120 {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
pdf.SetY(45)
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// 添加二维码标题
|
||||
@@ -1381,9 +1476,7 @@ func (pb *PageBuilder) addQRCodeImage(pdf *gofpdf.Fpdf, content string, chineseF
|
||||
qrSize := 40.0
|
||||
if pageHeight-currentY-bottomMargin < qrSize+20 {
|
||||
pdf.AddPage()
|
||||
pb.addHeader(pdf, chineseFontAvailable)
|
||||
pb.addWatermark(pdf, chineseFontAvailable)
|
||||
pdf.SetY(45)
|
||||
pdf.SetY(ContentStartYBelowHeader)
|
||||
}
|
||||
|
||||
// 生成二维码
|
||||
|
||||
@@ -434,4 +434,3 @@ func (m *PDFCacheManager) GetCacheStats() (map[string]interface{}, error) {
|
||||
"max_size": m.maxSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2078,69 +2078,55 @@ func (g *PDFGenerator) addHeader(pdf *gofpdf.Fpdf, chineseFontAvailable bool) {
|
||||
pdf.Line(15, 22, 75, 22)
|
||||
}
|
||||
|
||||
// addWatermark 添加水印(从左边开始向上倾斜45度,考虑可用区域)
|
||||
// addWatermark 添加水印:自左下角往右上角倾斜 45°,单条水印居中于页面,样式柔和
|
||||
func (g *PDFGenerator) addWatermark(pdf *gofpdf.Fpdf, chineseFontAvailable bool) {
|
||||
// 如果中文字体不可用,跳过水印(避免显示乱码)
|
||||
if !chineseFontAvailable {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存当前图形状态
|
||||
pdf.TransformBegin()
|
||||
defer pdf.TransformEnd()
|
||||
|
||||
// 获取页面尺寸和边距
|
||||
_, pageHeight := pdf.GetPageSize()
|
||||
pageWidth, pageHeight := pdf.GetPageSize()
|
||||
leftMargin, topMargin, _, bottomMargin := pdf.GetMargins()
|
||||
|
||||
// 计算实际可用区域高度
|
||||
usableHeight := pageHeight - topMargin - bottomMargin
|
||||
usableWidth := pageWidth - leftMargin*2
|
||||
|
||||
// 设置水印样式(使用中文字体)
|
||||
fontSize := 45.0
|
||||
|
||||
fontSize := 42.0
|
||||
pdf.SetFont("ChineseFont", "", fontSize)
|
||||
|
||||
// 设置灰色和透明度(加深水印,使其更明显)
|
||||
pdf.SetTextColor(180, 180, 180) // 深一点的灰色
|
||||
pdf.SetAlpha(0.25, "Normal") // 增加透明度,让水印更明显
|
||||
pdf.SetTextColor(150, 150, 150)
|
||||
pdf.SetAlpha(0.32, "Normal")
|
||||
|
||||
// 计算文字宽度
|
||||
textWidth := pdf.GetStringWidth(g.watermarkText)
|
||||
if textWidth == 0 {
|
||||
// 如果无法获取宽度(字体未注册),使用估算值(中文字符大约每个 fontSize/3 mm)
|
||||
textWidth = float64(len([]rune(g.watermarkText))) * fontSize / 3.0
|
||||
}
|
||||
|
||||
// 从左边开始,计算起始位置
|
||||
// 起始X:左边距
|
||||
// 起始Y:考虑水印文字长度和旋转后需要的空间
|
||||
startX := leftMargin
|
||||
startY := topMargin + textWidth*0.5 // 为旋转留出空间
|
||||
|
||||
// 移动到起始位置
|
||||
pdf.TransformTranslate(startX, startY)
|
||||
|
||||
// 向上倾斜45度(顺时针旋转45度,即-45度,或逆时针315度)
|
||||
pdf.TransformRotate(-45, 0, 0)
|
||||
|
||||
// 检查文字是否会超出可用区域(旋转后的对角线长度)
|
||||
rotatedDiagonal := math.Sqrt(textWidth*textWidth + fontSize*fontSize)
|
||||
if rotatedDiagonal > usableHeight*0.8 {
|
||||
// 如果太大,缩小字体
|
||||
fontSize = fontSize * usableHeight * 0.8 / rotatedDiagonal
|
||||
if rotatedDiagonal > usableHeight*0.75 {
|
||||
fontSize = fontSize * usableHeight * 0.75 / rotatedDiagonal
|
||||
pdf.SetFont("ChineseFont", "", fontSize)
|
||||
textWidth = pdf.GetStringWidth(g.watermarkText)
|
||||
if textWidth == 0 {
|
||||
textWidth = float64(len([]rune(g.watermarkText))) * fontSize / 3.0
|
||||
}
|
||||
rotatedDiagonal = math.Sqrt(textWidth*textWidth + fontSize*fontSize)
|
||||
}
|
||||
|
||||
// 从左边开始绘制水印文字
|
||||
startX := leftMargin
|
||||
startY := pageHeight - bottomMargin
|
||||
diagW := rotatedDiagonal * math.Cos(45*math.Pi/180)
|
||||
offsetX := (usableWidth - diagW) * 0.5
|
||||
startX += offsetX
|
||||
startY -= rotatedDiagonal * 0.5
|
||||
|
||||
pdf.TransformTranslate(startX, startY)
|
||||
pdf.TransformRotate(45, 0, 0)
|
||||
|
||||
pdf.SetXY(0, 0)
|
||||
pdf.CellFormat(textWidth, fontSize, g.watermarkText, "", 0, "L", false, 0, "")
|
||||
|
||||
// 恢复透明度和颜色
|
||||
pdf.SetAlpha(1.0, "Normal")
|
||||
pdf.SetTextColor(0, 0, 0) // 恢复为黑色
|
||||
pdf.SetTextColor(0, 0, 0)
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *ent
|
||||
|
||||
// 创建PDF文档 (A4大小,gofpdf v2 默认支持UTF-8)
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
// 优化边距,减少空白
|
||||
pdf.SetMargins(15, 25, 15)
|
||||
// 上边距与 ContentStartYBelowHeader 一致,这样自动分页后新页内容从 logo 下方开始,不被遮挡
|
||||
pdf.SetMargins(15, ContentStartYBelowHeader, 15)
|
||||
|
||||
// 加载黑体字体(用于所有内容,除了水印)
|
||||
// 注意:此时工作目录应该是根目录(/),这样gofpdf处理路径时就能正确解析
|
||||
@@ -176,9 +176,22 @@ func (g *PDFGeneratorRefactored) generatePDF(product *entities.Product, doc *ent
|
||||
// 创建页面构建器
|
||||
pageBuilder := NewPageBuilder(g.logger, g.fontManager, g.textProcessor, g.markdownProc, g.tableParser, g.tableRenderer, g.jsonProcessor, g.logoPath, g.watermarkText)
|
||||
|
||||
// 添加第一页(产品信息)
|
||||
// 页眉只画 logo+横线。水印用 SetFooterFunc 画:gofpdf 在每页内容画完后再调 Footer,水印最后画,z 层在最上,不会被表格等盖住
|
||||
pdf.SetHeaderFunc(func() {
|
||||
pageBuilder.addHeader(pdf, chineseFontAvailable)
|
||||
})
|
||||
pdf.SetFooterFunc(func() {
|
||||
pageBuilder.addWatermark(pdf, chineseFontAvailable)
|
||||
})
|
||||
|
||||
// 添加第一页(封面:产品信息 + 产品描述 + 价格)
|
||||
pageBuilder.AddFirstPage(pdf, product, doc, chineseFontAvailable)
|
||||
|
||||
// 产品详情单独一页(左对齐,段前两空格)
|
||||
if product.Content != "" {
|
||||
pageBuilder.AddProductContentPage(pdf, product, chineseFontAvailable)
|
||||
}
|
||||
|
||||
// 如果是组合包,需要特殊处理:先渲染所有文档,最后统一添加二维码
|
||||
if product.IsPackage {
|
||||
// 如果有关联的文档,添加接口文档页面(但不包含二维码和说明,后面统一添加)
|
||||
|
||||
@@ -90,6 +90,22 @@ func (tp *TextProcessor) StripHTML(text string) string {
|
||||
return text
|
||||
}
|
||||
|
||||
// HTMLToPlainWithBreaks 将 HTML 转为纯文本并保留富文本换行效果(<p><br><div> 等变为换行)
|
||||
// 用于在 PDF 中还原段落与换行,避免内容挤成一团
|
||||
func (tp *TextProcessor) HTMLToPlainWithBreaks(text string) string {
|
||||
text = html.UnescapeString(text)
|
||||
// 块级结束标签转为换行
|
||||
text = regexp.MustCompile(`(?i)</(p|div|br|tr|li|h[1-6])>\s*`).ReplaceAllString(text, "\n")
|
||||
// <br> 自闭合
|
||||
text = regexp.MustCompile(`(?i)<br\s*/?>\s*`).ReplaceAllString(text, "\n")
|
||||
// 剩余标签移除
|
||||
text = regexp.MustCompile(`<[^>]+>`).ReplaceAllString(text, "")
|
||||
// 连续空白/换行压缩为最多两个换行(段间距)
|
||||
text = regexp.MustCompile(`[ \t]+`).ReplaceAllString(text, " ")
|
||||
text = regexp.MustCompile(`\n{3,}`).ReplaceAllString(text, "\n\n")
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// RemoveMarkdownSyntax 移除markdown语法,保留纯文本
|
||||
func (tp *TextProcessor) RemoveMarkdownSyntax(text string) string {
|
||||
// 移除粗体标记 **text** 或 __text__
|
||||
|
||||
BIN
resources/pdf/fonts/WenYuanSerifSC-Bold.ttf
Normal file
BIN
resources/pdf/fonts/WenYuanSerifSC-Bold.ttf
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,811 +0,0 @@
|
||||
[
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "人企关系加强版",
|
||||
"sort": 1
|
||||
},
|
||||
"data": {
|
||||
"apiID": "QYGL3F8E",
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"abnormal_info": {},
|
||||
"basicInfo": {
|
||||
"apprdate": "2025-11-27",
|
||||
"base": "han",
|
||||
"candate": "",
|
||||
"city": "xxxxx",
|
||||
"companyOrgType": "有限责任公司(自然人投资或控股)",
|
||||
"creditCode": "xxxxx",
|
||||
"district": "秀xxx区",
|
||||
"estiblishTime": "2024-06-20",
|
||||
"his_staffList": {
|
||||
"result": [
|
||||
{
|
||||
"name": "xxxx",
|
||||
"type": "2",
|
||||
"typeJoin": [
|
||||
"执行董事兼总经理"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"industry": "xxxxxx",
|
||||
"industry_code": "L",
|
||||
"legalPersonName": "xxxx",
|
||||
"name": "xxxxx有限公司",
|
||||
"nic_code": "L7281",
|
||||
"nic_name": "租赁和商务服务业-商务服务业-会议、展览及相关服务-科技会展服务",
|
||||
"opscope": "许可经营项目:在线数据处理与交易处理业务(经营类电子商务);互联网游戏服务;第二类增值电信业务;互联网信息服务(许可经营项目凭许可证件经营)一般经营项目:品牌管理;5G通信技术服务;人工智能应用软件开发;互联网安全服务;量子计算技术服务;技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广;网络技术服务;专业设计服务;互联网数据服务;互联网销售(除销售需要许可的商品);食品互联网销售(仅销售预包装食品);软件开发;动漫游戏开发;计算机软硬件及辅助设备零售;计算机软硬件及辅助设备批发;计算器设备销售;机械设备销售;五金产品零售;五金产品批发;电子产品销售;人工智能硬件销售;通信设备销售;光通信设备销售;通信设备制造;信息系统集成服务;图文设计制作;广告设计、代理;广告发布;数字内容制作服务(不含出版发行);数字文化创意软件开发;软件销售;市场营销策划;企业管理咨询;信息咨询服务(不含许可类信息咨询服务);市场调查(不含涉外调查);工业设计服务;玩具销售;化妆品零售;化妆品批发;摄像及视频制作服务;平面设计;法律咨询(不含依法须律师事务所执业许可的业务);旅游开发项目策划咨询;体育用品及器材批发;体育用品及器材零售;户外用品销售;体育赛事策划;体育健康服务;组织体育表演活动;体育中介代理服务;信息技术咨询服务;数据处理服务;数据处理和存储支持服务;大数据服务;云计算装备技术服务;电子、机械设备维护(不含特种设备);智能机器人的研发;货物进出口;技术进出口;食品进出口(经营范围中的一般经营项目依法自主开展经营活动,通过国家企业信用信息公示系统(海南)向社会公示)",
|
||||
"province": "xxx省",
|
||||
"reccap": 0,
|
||||
"reccapcur": "人民币",
|
||||
"regCapital": "100.000000万人民币",
|
||||
"regCapitalCurrency": "人民币",
|
||||
"regNumber": "4601xxxxx1916",
|
||||
"regStatus": "存续(在营、开业、在册)",
|
||||
"regorg": "xxxxxx督管理局",
|
||||
"revdate": "",
|
||||
"type": "1"
|
||||
},
|
||||
"financing_history": {},
|
||||
"fsource": "1",
|
||||
"his_stockHolderItem": {
|
||||
"investDate": "2025-11-27",
|
||||
"investRate": "",
|
||||
"orgHolderName": "xxx",
|
||||
"orgHolderType": "xxxx人",
|
||||
"subscriptAmt": ""
|
||||
},
|
||||
"invest_history": {},
|
||||
"lawsuit_info": {
|
||||
"entout": {
|
||||
"msg": "没有找到"
|
||||
},
|
||||
"sxbzxr": {
|
||||
"msg": "没有找到"
|
||||
},
|
||||
"xgbzxr": {
|
||||
"msg": "没有找到"
|
||||
}
|
||||
},
|
||||
"orgName": "海xxxx限公司",
|
||||
"own_tax": {},
|
||||
"pName": "xxx",
|
||||
"punishment_info": {},
|
||||
"relationship": [
|
||||
"his_sh",
|
||||
"his_tm"
|
||||
],
|
||||
"tax_contravention": {}
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "全景雷达",
|
||||
"sort": 2
|
||||
},
|
||||
"data": {
|
||||
"apiID": "JRZQ7F1A",
|
||||
"data": {
|
||||
"apply_report_detail": {
|
||||
"A22160001": "503",
|
||||
"A22160002": "76",
|
||||
"A22160003": "38",
|
||||
"A22160004": "11",
|
||||
"A22160005": "13",
|
||||
"A22160006": "90",
|
||||
"A22160007": "2023-05",
|
||||
"A22160008": "15",
|
||||
"A22160009": "33",
|
||||
"A22160010": "48"
|
||||
},
|
||||
"behavior_report_detail": {
|
||||
"B22170001": "547",
|
||||
"B22170002": "1",
|
||||
"B22170003": "3",
|
||||
"B22170004": "7",
|
||||
"B22170005": "20",
|
||||
"B22170006": "32",
|
||||
"B22170007": "(0,500)",
|
||||
"B22170008": "[2000,3000)",
|
||||
"B22170009": "[10000,20000)",
|
||||
"B22170010": "[30000,50000)",
|
||||
"B22170011": "[50000,+)",
|
||||
"B22170012": "11",
|
||||
"B22170013": "7",
|
||||
"B22170014": "2",
|
||||
"B22170015": "0",
|
||||
"B22170016": "1",
|
||||
"B22170017": "2",
|
||||
"B22170018": "3",
|
||||
"B22170019": "6",
|
||||
"B22170020": "7",
|
||||
"B22170021": "6",
|
||||
"B22170022": "7",
|
||||
"B22170023": "0",
|
||||
"B22170024": "0",
|
||||
"B22170025": "4",
|
||||
"B22170026": "8",
|
||||
"B22170027": "9",
|
||||
"B22170028": "4",
|
||||
"B22170029": "5",
|
||||
"B22170030": "5",
|
||||
"B22170031": "[3000,5000)",
|
||||
"B22170032": "[5000,10000)",
|
||||
"B22170033": "[5000,10000)",
|
||||
"B22170034": "70%",
|
||||
"B22170035": "4",
|
||||
"B22170036": "14",
|
||||
"B22170037": "21",
|
||||
"B22170038": "35",
|
||||
"B22170039": "102",
|
||||
"B22170040": "(0,500)",
|
||||
"B22170041": "[500,1000)",
|
||||
"B22170042": "[500,1000)",
|
||||
"B22170043": "[10000,20000)",
|
||||
"B22170044": "[30000,50000)",
|
||||
"B22170045": "4",
|
||||
"B22170046": "6",
|
||||
"B22170047": "8",
|
||||
"B22170048": "35",
|
||||
"B22170049": "168",
|
||||
"B22170050": "(7,15]",
|
||||
"B22170051": "82",
|
||||
"B22170052": "24",
|
||||
"B22170053": "720",
|
||||
"B22170054": "2023-04"
|
||||
},
|
||||
"current_report_detail": {
|
||||
"C22180001": "0",
|
||||
"C22180002": "0",
|
||||
"C22180003": "0",
|
||||
"C22180004": "0",
|
||||
"C22180005": "0",
|
||||
"C22180006": "0",
|
||||
"C22180007": "5",
|
||||
"C22180008": "9",
|
||||
"C22180009": "12600",
|
||||
"C22180010": "6120",
|
||||
"C22180011": "10600",
|
||||
"C22180012": "80"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "特殊名单验证B",
|
||||
"sort": 3
|
||||
},
|
||||
"data": {
|
||||
"apiID": "JRZQ8A2D",
|
||||
"data": {
|
||||
"cell": {
|
||||
"nbank_bad": "0",
|
||||
"nbank_bad_allnum": "1",
|
||||
"nbank_bad_time": "2",
|
||||
"nbank_finlea_lost": "0",
|
||||
"nbank_finlea_lost_allnum": "4",
|
||||
"nbank_finlea_lost_time": "5",
|
||||
"nbank_lost": "0",
|
||||
"nbank_lost_allnum": "4",
|
||||
"nbank_lost_time": "5",
|
||||
"nbank_other_bad": "0",
|
||||
"nbank_other_bad_allnum": "1",
|
||||
"nbank_other_bad_time": "2"
|
||||
},
|
||||
"id": {
|
||||
"court_executed": "0",
|
||||
"court_executed_allnum": "5",
|
||||
"court_executed_time": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"feature": {
|
||||
"featureName": "个人司法涉诉查询",
|
||||
"sort": 4
|
||||
},
|
||||
"data": {
|
||||
"apiID": "FLXG7E8F",
|
||||
"data": {
|
||||
"judicial_data": {
|
||||
"breachCaseList": [
|
||||
{
|
||||
"caseNumber": "(2021)京0113执****号",
|
||||
"concreteDetails": "有履⾏能⼒⽽拒不履⾏⽣效法律⽂书确定义务",
|
||||
"enforcementBasisNumber": "(2020)京0113⺠初9****",
|
||||
"enforcementBasisOrganization": "京海市顺义区⼈⺠法院",
|
||||
"estimatedJudgementAmount": 109455,
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"fileDate": "2021-02-23",
|
||||
"fulfillStatus": "全部未履⾏",
|
||||
"id": "f343e0d314e840d93684fa9a90f144cc",
|
||||
"issueDate": "2021-02-23",
|
||||
"obligation": "被告靳帅偿还原告王丹霞借元,于本判决⽣效之⽇起七⽇内执⾏。",
|
||||
"province": "北京",
|
||||
"sex": "男性"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2022)京0113执5190号",
|
||||
"concreteDetails": "有履⾏能⼒⽽拒不履⾏⽣效法律⽂书确定义务",
|
||||
"enforcementBasisNumber": "(2022)京0113刑初****",
|
||||
"enforcementBasisOrganization": "京海市顺义区⼈⺠法院",
|
||||
"estimatedJudgementAmount": 18110,
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"fileDate": "2022-07-12",
|
||||
"fulfillStatus": "全部未履⾏",
|
||||
"id": "6cc2453f4e8cccf3ecd441ae08dd2183",
|
||||
"issueDate": "2022-07-12",
|
||||
"obligation": "⼀、被告⼈靳帅犯盗窃罪,判处有期徒刑⼀年三个⽉,并处罚⾦⼈⺠币五千元(刑期从判决执⾏之⽇起计算,判决执⾏以前先⾏羁押的,羁押⼀⽇折抵刑期⼀⽇,即⾃2022年3⽉6⽇起⾄2023年6⽉5⽇⽌。罚⾦于判决⽣效之⽇起五⽇内缴纳)。\\n⼆、责令被告⼈靳帅退赔被害⼈孙学⺠的经济损失⼈⺠币⼗元,退赔被害⼈张树起的经济损失⼈⺠币五百元,退赔被害⼈冯⽂⻰的经济损失⼈⺠币⼀万⼆千六百元。",
|
||||
"province": "北京",
|
||||
"sex": "男性"
|
||||
}
|
||||
],
|
||||
"consumptionRestrictionList": [
|
||||
{
|
||||
"caseNumber": "(2023)京0113执*****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "0b733c6c503f740663422e44bc434a66",
|
||||
"issueDate": "2023-11-20"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2021)京0113执****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "3fa335ec744dfb1d720f996e9d2b6e12",
|
||||
"issueDate": "2021-08-10"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2022)京0113执****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "84fa0cf34f947eb0afd2f54ebe589e35",
|
||||
"issueDate": "2022-07-24"
|
||||
},
|
||||
{
|
||||
"caseNumber": "(2021)京0113执****号",
|
||||
"executiveCourt": "京海市顺义区⼈⺠法院",
|
||||
"id": "2e53a8808313ba87f15bf30ae76cd2d6",
|
||||
"issueDate": "2021-11-19"
|
||||
}
|
||||
],
|
||||
"lawsuitStat": {
|
||||
"administrative": {},
|
||||
"bankrupt": {},
|
||||
"cases_tree": {
|
||||
"civil": [
|
||||
{
|
||||
"c_ah": "2013年⻄⺠初字第*****号",
|
||||
"case_type": 300,
|
||||
"n_ajbs": "257ebb5c348de00883c872d636cf3128",
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "2013年⻄⺠初字第0***号",
|
||||
"case_type": 300,
|
||||
"n_ajbs": "b6d8144d729f7811f4ea7838ef69baa7",
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2020)京0113⺠初****号",
|
||||
"case_type": 300,
|
||||
"n_ajbs": "5a0867d91ce580d1239e1f2063912584",
|
||||
"next": {
|
||||
"c_ah": "(2021)京0113执****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "54e45b851f5baedc7d249ab755e39fbe",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
}
|
||||
],
|
||||
"criminal": [
|
||||
{
|
||||
"c_ah": "2009年顺刑初字第*****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "e084cc09e364a6c2c02f82bd49a3bcfd",
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2021)京0113刑初****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "08c9087760d19e4e46ea0a5e1ff8907f",
|
||||
"next": {
|
||||
"c_ah": "(2021)京0113执****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "3e8392c51bbc1b7fb8e050284c89d220",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2022)京0113刑初****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "1da42d08e89cf1907b0ab30239437060",
|
||||
"next": {
|
||||
"c_ah": "(2022)京0113执****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "c345a052409a2c0ebaecd6cee45b8050",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
},
|
||||
{
|
||||
"c_ah": "(2023)京0113刑****号",
|
||||
"case_type": 200,
|
||||
"n_ajbs": "91b1aa92abba978b9bb583de92445045",
|
||||
"next": {
|
||||
"c_ah": "(2023)京0113执1****号",
|
||||
"case_type": 1000,
|
||||
"n_ajbs": "8dda746bb87c72f76d49a2cacee0efa0",
|
||||
"stage_type": 5
|
||||
},
|
||||
"stage_type": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"civil": {
|
||||
"cases": [
|
||||
{
|
||||
"c_ah": "2013年⻄⺠初字第******号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "中国建设银⾏股份有限公司京海市分⾏",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "原告"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告"
|
||||
}
|
||||
],
|
||||
"c_id": "ea3566e092ef9bf659659b2c07855d3e",
|
||||
"c_slfsxx": "-1,,,;1,2013-06-19 09:00:00,1,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2013-06-18",
|
||||
"d_larq": "2013-03-08",
|
||||
"n_ajbs": "b6d8144d729f7811f4ea7838ef69baa7",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⺠事⼀审",
|
||||
"n_crc": 451683830,
|
||||
"n_jaay": "合同、准合同纠纷",
|
||||
"n_jaay_tree": "合同、准合同纠纷,合同纠纷,银⾏卡纠纷,信⽤卡纠纷",
|
||||
"n_jabdje": "3304.16",
|
||||
"n_jabdje_level": 1,
|
||||
"n_jafs": "准予撤诉",
|
||||
"n_jbfy": "京海市⻄城区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "合同、准合同纠纷",
|
||||
"n_laay_tree": "合同、准合同纠纷,合同纠纷,银⾏卡纠纷,信⽤卡纠纷",
|
||||
"n_pj_victory": "未知",
|
||||
"n_qsbdje": "3304.16",
|
||||
"n_qsbdje_level": 1,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告",
|
||||
"n_ssdw_ys": "被告"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2020)京0113⺠初*****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "王丹霞",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "原告"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告"
|
||||
}
|
||||
],
|
||||
"c_id": "df1fd042f1545a51c6460d0cb4005140",
|
||||
"c_slfsxx": "1,2020-11-25 11:10:17,诉调第⼗⼀法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2020-12-04",
|
||||
"d_larq": "2020-07-29",
|
||||
"n_ajbs": "5a0867d91ce580d1239e1f2063912584",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⺠事⼀审",
|
||||
"n_crc": 4035395111,
|
||||
"n_jaay": "合同、准合同纠纷",
|
||||
"n_jaay_tag": "合同纠纷",
|
||||
"n_jaay_tree": "合同、准合同纠纷,合同纠纷,借款合同纠纷,⺠间借贷纠纷",
|
||||
"n_jabdje": 109455,
|
||||
"n_jabdje_level": 11,
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "合同、准合同纠纷",
|
||||
"n_laay_tag": "合同纠纷",
|
||||
"n_laay_tree": "合同、准合同纠纷,合同纠纷,借款合同纠纷,⺠间借贷纠纷",
|
||||
"n_pj_victory": "未知",
|
||||
"n_qsbdje": 110000,
|
||||
"n_qsbdje_level": 11,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告",
|
||||
"n_ssdw_ys": "被告"
|
||||
}
|
||||
],
|
||||
"count": {
|
||||
"area_stat": "京海市(3)",
|
||||
"ay_stat": "合同、准合同纠纷(3)",
|
||||
"count_beigao": 3,
|
||||
"count_jie_beigao": 3,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 3,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 3,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "准予撤诉(2),判决(1)",
|
||||
"larq_stat": "2013(2),2020(1)",
|
||||
"money_beigao": 11,
|
||||
"money_jie_beigao": 11,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 11,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 11,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
}
|
||||
},
|
||||
"count": {
|
||||
"area_stat": "京海市(11)",
|
||||
"ay_stat": "侵犯财产罪(4),刑事(3),合同、准合同纠纷(3),未知(1)",
|
||||
"count_beigao": 11,
|
||||
"count_jie_beigao": 11,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 11,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 11,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "判决(5),终结本次执⾏程序(4),准予撤诉(2)",
|
||||
"larq_stat": "2009(1),2013(2),2020(1),2021(3),2022(2),2023(2)",
|
||||
"money_beigao": 11,
|
||||
"money_jie_beigao": 11,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 11,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 11,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
},
|
||||
"crc": 3714068012,
|
||||
"criminal": {
|
||||
"cases": [
|
||||
{
|
||||
"c_ah": "(2021)京0113刑初*****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "44bc6ccd90fada8e585e27f86700696c",
|
||||
"c_slfsxx": "1,2021-09-23 09:48:23,第三⼗七法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2021-09-23",
|
||||
"d_larq": "2021-09-16",
|
||||
"n_ajbs": "08c9087760d19e4e46ea0a5e1ff8907f",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "刑事⼀审",
|
||||
"n_bqqpcje_level": 0,
|
||||
"n_ccxzxje_level": 0,
|
||||
"n_crc": 3782814141,
|
||||
"n_dzzm": "侵犯财产罪",
|
||||
"n_dzzm_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_fzje_level": 0,
|
||||
"n_jaay": "侵犯财产罪",
|
||||
"n_jaay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "侵犯财产罪",
|
||||
"n_laay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_pcjg": "给予刑事处罚",
|
||||
"n_pcpcje_level": 0,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告⼈",
|
||||
"n_ssdw_ys": "被告⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2022)京0113刑初****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "8851b2565cd27bc09a00a8ecd82b3224",
|
||||
"c_slfsxx": "1,2022-06-08 09:38:41,第四⼗法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2022-06-17",
|
||||
"d_larq": "2022-06-02",
|
||||
"n_ajbs": "1da42d08e89cf1907b0ab30239437060",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "刑事⼀审",
|
||||
"n_bqqpcje_level": 0,
|
||||
"n_ccxzxje_level": 0,
|
||||
"n_crc": 168162812,
|
||||
"n_dzzm": "侵犯财产罪",
|
||||
"n_dzzm_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_fzje_level": 0,
|
||||
"n_jaay": "侵犯财产罪",
|
||||
"n_jaay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "侵犯财产罪",
|
||||
"n_laay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_pcjg": "给予刑事处罚",
|
||||
"n_pcpcje_level": 0,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告⼈",
|
||||
"n_ssdw_ys": "被告⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2023)京0113刑****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被告⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "82c3a2095c4ee2102fe156fc6cd5c77c",
|
||||
"c_slfsxx": "1,2023-10-27 09:19:41,第三⼗七法庭,1",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2023-10-27",
|
||||
"d_larq": "2023-10-11",
|
||||
"n_ajbs": "91b1aa92abba978b9bb583de92445045",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "刑事⼀审",
|
||||
"n_bqqpcje_level": 0,
|
||||
"n_ccxzxje_level": 0,
|
||||
"n_crc": 659651411,
|
||||
"n_dzzm": "侵犯财产罪",
|
||||
"n_dzzm_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_fzje_level": 0,
|
||||
"n_jaay": "侵犯财产罪",
|
||||
"n_jaay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_jafs": "判决",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "侵犯财产罪",
|
||||
"n_laay_tree": "侵犯财产罪,盗窃罪",
|
||||
"n_pcjg": "给予刑事处罚",
|
||||
"n_pcpcje_level": 0,
|
||||
"n_slcx": "⼀审",
|
||||
"n_ssdw": "被告⼈",
|
||||
"n_ssdw_ys": "被告⼈"
|
||||
}
|
||||
],
|
||||
"count": {
|
||||
"area_stat": "京海市(4)",
|
||||
"ay_stat": "侵犯财产罪(4)",
|
||||
"count_beigao": 4,
|
||||
"count_jie_beigao": 4,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 4,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 4,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "判决(4)",
|
||||
"larq_stat": "2009(1),2021(1),2022(1),2023(1)",
|
||||
"money_beigao": 0,
|
||||
"money_jie_beigao": 0,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 0,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 0,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
}
|
||||
},
|
||||
"implement": {
|
||||
"cases": [
|
||||
{
|
||||
"c_ah": "(2021)京0113执2***",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "王丹霞",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_gkws_glah": "(2020)京0113⺠初***号",
|
||||
"c_id": "4904d5bf89ca75a79bf7401727080c03",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2021-08-19",
|
||||
"d_larq": "2021-02-23",
|
||||
"n_ajbs": "54e45b851f5baedc7d249ab755e39fbe",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 2505253178,
|
||||
"n_jaay": "⺠事",
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "⺠事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 111260,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2021)京0113执***号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "京海市顺义区⼈⺠法院",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "435d6483338571526e6ebb0308dc6d04",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2021-11-25",
|
||||
"d_larq": "2021-10-26",
|
||||
"n_ajbs": "3e8392c51bbc1b7fb8e050284c89d220",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 1948524411,
|
||||
"n_jaay": "刑事",
|
||||
"n_jabdje": 3876,
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "刑事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 3876,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2022)京0113执****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "京海市顺义区⼈⺠法院",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_id": "4683b25207c45768ed9bcead28b51036",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2022-08-04",
|
||||
"d_larq": "2022-07-12",
|
||||
"n_ajbs": "c345a052409a2c0ebaecd6cee45b8050",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 3747572709,
|
||||
"n_jaay": "刑事",
|
||||
"n_jabdje": 18110,
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "刑事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 18110,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_ah": "(2023)京0113执*****号",
|
||||
"c_dsrxx": [
|
||||
{
|
||||
"c_mc": "京海市顺义区⼈⺠法院",
|
||||
"n_dsrlx": "企业组织",
|
||||
"n_ssdw": "申请执⾏⼈"
|
||||
},
|
||||
{
|
||||
"c_mc": "靳帅",
|
||||
"n_dsrlx": "⾃然⼈",
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"c_gkws_glah": "(2023)京0113刑****号",
|
||||
"c_id": "30285f31d30a24a2a41cc59fcb0928bc",
|
||||
"c_ssdy": "京海市",
|
||||
"d_jarq": "2024-01-02",
|
||||
"d_larq": "2023-11-20",
|
||||
"n_ajbs": "8dda746bb87c72f76d49a2cacee0efa0",
|
||||
"n_ajjzjd": "已结案",
|
||||
"n_ajlx": "⾸次执⾏",
|
||||
"n_crc": 2098789290,
|
||||
"n_jaay": "刑事",
|
||||
"n_jabdje": 4670,
|
||||
"n_jafs": "终结本次执⾏程序",
|
||||
"n_jbfy": "京海市顺义区⼈⺠法院",
|
||||
"n_jbfy_cj": "基层法院",
|
||||
"n_laay": "刑事",
|
||||
"n_sjdwje": 0,
|
||||
"n_sqzxbdje": 4670,
|
||||
"n_ssdw": "被执⾏⼈"
|
||||
}
|
||||
],
|
||||
"count": {
|
||||
"area_stat": "京海市(4)",
|
||||
"ay_stat": "刑事(3),未知(1)",
|
||||
"count_beigao": 4,
|
||||
"count_jie_beigao": 4,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 4,
|
||||
"count_jie_yuangao": 0,
|
||||
"count_other": 0,
|
||||
"count_total": 4,
|
||||
"count_wei_beigao": 0,
|
||||
"count_wei_other": 0,
|
||||
"count_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"count_yuangao": 0,
|
||||
"jafs_stat": "终结本次执⾏程序(4)",
|
||||
"larq_stat": "2021(2),2022(1),2023(1)",
|
||||
"money_beigao": 3,
|
||||
"money_jie_beigao": 3,
|
||||
"money_jie_other": 0,
|
||||
"money_jie_total": 3,
|
||||
"money_jie_yuangao": 0,
|
||||
"money_other": 0,
|
||||
"money_total": 3,
|
||||
"money_wei_beigao": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"money_wei_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"money_yuangao": 0
|
||||
}
|
||||
},
|
||||
"preservation": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Reference in New Issue
Block a user