Files
tyapi-server/internal/shared/pdf/qygl_report_pdf.go
2026-03-11 17:23:09 +08:00

1812 lines
52 KiB
Go
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.

package pdf
import (
"bytes"
"context"
"fmt"
"strings"
"github.com/jung-kurt/gofpdf/v2"
"go.uber.org/zap"
)
// GenerateQYGLReportPDF 使用 gofpdf 根据 QYGLJ1U9 聚合结果生成企业全景报告 PDF
// 尽量复刻 qiye.html 中的板块顺序和展示逻辑。
// report 参数通常是从 JSON 反序列化得到的 map[string]interface{}。
func GenerateQYGLReportPDF(_ context.Context, logger *zap.Logger, report map[string]interface{}) ([]byte, error) {
if logger == nil {
logger = zap.NewNop()
}
pdf := gofpdf.New("P", "mm", "A4", "")
// 整体页边距和自动分页底部预留稍微加大,整体更「松」一点
pdf.SetMargins(15, 25, 15)
pdf.SetAutoPageBreak(true, 22)
// 加载中文字体
fontManager := NewFontManager(logger)
chineseFontLoaded := fontManager.LoadChineseFont(pdf)
fontName := "ChineseFont"
if !chineseFontLoaded {
// 回退:使用内置核心字体(英文),中文可能会显示为方块
fontName = "Arial"
}
// 整体默认正文字体稍微放大
pdf.SetFont(fontName, "", 14)
// 头部信息,对齐 qiye.html
entName := getString(report, "entName")
if entName == "" {
entName = "未知企业"
}
creditCode := getString(report, "creditCode")
if creditCode == "" {
creditCode = getStringFromMap(report, "basic", "creditCode")
}
basic := getMap(report, "basic")
entStatus := getString(basic, "status")
if entStatus == "" {
entStatus = getString(basic, "entStatus")
}
entType := getString(basic, "entType")
reportTime := getString(report, "reportTime")
riskOverview := getMap(report, "riskOverview")
riskScore := getString(riskOverview, "riskScore")
riskLevel := getString(riskOverview, "riskLevel")
// 首页头部
pdf.AddPage()
// 预留更多顶部空白,让标题整体再下移一些
pdf.Ln(20)
// 顶部大标题:居中、加粗、蓝色,更大字号
pdf.SetFont(fontName, "B", 34)
pdf.SetTextColor(37, 99, 235)
pdf.CellFormat(0, 18, "企业全景报告", "", 1, "C", false, 0, "")
pdf.Ln(10)
// 恢复正文颜色
pdf.SetTextColor(0, 0, 0)
pdf.SetFont(fontName, "", 18)
pdf.MultiCell(0, 9, fmt.Sprintf("企业名称:%s", entName), "", "L", false)
pdf.SetFont(fontName, "", 14)
pdf.MultiCell(0, 7, fmt.Sprintf("统一社会信用代码:%s", safePlaceholder(creditCode)), "", "L", false)
pdf.MultiCell(0, 7, fmt.Sprintf("经营状态:%s", safePlaceholder(entStatus)), "", "L", false)
pdf.MultiCell(0, 7, fmt.Sprintf("企业类型:%s", safePlaceholder(entType)), "", "L", false)
if reportTime != "" {
pdf.MultiCell(0, 7, fmt.Sprintf("报告生成时间:%s", reportTime), "", "L", false)
}
// 综合风险评分(使用 riskOverview
pdf.Ln(6)
pdfSubTitle(pdf, fontName, "综合风险评分")
pdf.SetFont(fontName, "", 13)
if riskScore != "" {
pdf.MultiCell(0, 7, fmt.Sprintf("风险得分:%s", riskScore), "", "L", false)
}
if riskLevel != "" {
pdf.MultiCell(0, 7, fmt.Sprintf("风险等级:%s", riskLevel), "", "L", false)
}
// 头部风险标签
if tags, ok := riskOverview["tags"].([]interface{}); ok && len(tags) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "", 11)
pdf.MultiCell(0, 5, "风险标签:", "", "L", false)
for _, t := range tags {
pdf.MultiCell(0, 5, fmt.Sprintf("· %v", t), "", "L", false)
}
}
// 对齐 qiye.html 的板块顺序
sections := []struct {
key string
title string
}{
{"riskOverview", "风险情况(综合分析)"},
{"basic", "一、主体概览(企业基础信息)"},
{"branches", "二、分支机构"},
{"shareholding", "三、股权与控制"},
{"controller", "四、实际控制人"},
{"beneficiaries", "五、最终受益人"},
{"investments", "六、对外投资"},
{"guarantees", "七、对外担保"},
{"management", "八、人员与组织"},
{"assets", "九、资产与经营(年报)"},
{"licenses", "十、行政许可与资质"},
{"activities", "十一、经营活动"},
{"inspections", "十二、抽查检查"},
{"risks", "十三、风险与合规"},
{"timeline", "十四、发展时间线"},
{"listed", "十五、上市信息"},
}
for _, s := range sections {
switch s.key {
case "riskOverview":
renderPDFRiskOverview(pdf, fontName, s.title, riskOverview)
case "basic":
renderPDFBasic(pdf, fontName, s.title, basic, entName, creditCode)
case "branches":
renderPDFBranches(pdf, fontName, s.title, getSlice(report, "branches"))
case "shareholding":
renderPDFShareholding(pdf, fontName, s.title, getMap(report, "shareholding"))
case "controller":
renderPDFController(pdf, fontName, s.title, getMap(report, "controller"))
case "beneficiaries":
renderPDFBeneficiaries(pdf, fontName, s.title, getSlice(report, "beneficiaries"))
case "investments":
renderPDFInvestments(pdf, fontName, s.title, getMap(report, "investments"))
case "guarantees":
renderPDFGuarantees(pdf, fontName, s.title, getSlice(report, "guarantees"))
case "management":
renderPDFManagement(pdf, fontName, s.title, getMap(report, "management"))
case "assets":
renderPDFAssets(pdf, fontName, s.title, getMap(report, "assets"))
case "licenses":
renderPDFLicenses(pdf, fontName, s.title, getMap(report, "licenses"))
case "activities":
renderPDFActivities(pdf, fontName, s.title, getMap(report, "activities"))
case "inspections":
renderPDFInspections(pdf, fontName, s.title, getSlice(report, "inspections"))
case "risks":
renderPDFRisks(pdf, fontName, s.title, getMap(report, "risks"))
case "timeline":
renderPDFTimeline(pdf, fontName, s.title, getSlice(report, "timeline"))
case "listed":
renderPDFListed(pdf, fontName, s.title, getMap(report, "listed"))
}
}
// 输出为字节
var buf bytes.Buffer
if err := pdf.Output(&buf); err != nil {
return nil, fmt.Errorf("输出企业全景报告 PDF 失败: %w", err)
}
return buf.Bytes(), nil
}
// 辅助方法
func getString(m map[string]interface{}, key string) string {
if m == nil {
return ""
}
if v, ok := m[key]; ok && v != nil {
if s, ok := v.(string); ok {
return s
}
return fmt.Sprint(v)
}
return ""
}
func getStringFromMap(root map[string]interface{}, field string, key string) string {
child := getMap(root, field)
return getString(child, key)
}
func getMap(m map[string]interface{}, key string) map[string]interface{} {
if m == nil {
return map[string]interface{}{}
}
if v, ok := m[key]; ok && v != nil {
if mm, ok := v.(map[string]interface{}); ok {
return mm
}
}
return map[string]interface{}{}
}
func getSlice(m map[string]interface{}, key string) []interface{} {
if m == nil {
return nil
}
if v, ok := m[key]; ok && v != nil {
if arr, ok := v.([]interface{}); ok {
return arr
}
}
return nil
}
func writeKeyValue(pdf *gofpdf.Fpdf, fontName, label, value string) {
if value == "" {
return
}
// 计算表格布局:左侧标签列 + 右侧内容列(模块内数据统一用表格框展示)
pageW, pageH := pdf.GetPageSize()
lMargin, _, rMargin, bMargin := pdf.GetMargins()
labelW := 40.0
valueW := pageW - lMargin - rMargin - labelW
// 行高整体拉宽一点
lineH := 10.0
// 当前起始坐标(统一从左边距起,栅格化对齐)
x := lMargin
y := pdf.GetY()
// 预计算内容高度:使用 SplitLines 获取行数,避免长内容导致单元格高度不够
lines := pdf.SplitLines([]byte(value), valueW)
rowH := float64(len(lines)) * lineH
if rowH < lineH {
rowH = lineH
}
// 轻微增加上下留白,避免文字贴边
rowH += 2
// 如剩余空间不足,先换到新页再画整行,避免表格跨页导致内容重叠
if y+rowH > pageH-bMargin {
pdf.AddPage()
y = pdf.GetY()
pdf.SetXY(lMargin, y)
}
// 画出整行的两个单元格边框
// 使用黑色描边,左侧标签单元格填充标题蓝色背景
pdf.SetDrawColor(0, 0, 0)
pdf.SetFillColor(91, 155, 213)
pdf.Rect(x, y, labelW, rowH, "FD") // 标签:填充+描边
pdf.Rect(x+labelW, y, valueW, rowH, "D") // 值:仅描边
// 写入标签单元格
pdf.SetXY(x, y)
pdf.SetFont(fontName, "B", 13)
pdf.SetTextColor(255, 255, 255)
pdf.CellFormat(labelW, rowH, fmt.Sprintf("%s", label), "", 0, "L", false, 0, "")
// 写入值单元格(自动换行)
pdf.SetXY(x+labelW, y)
pdf.SetFont(fontName, "", 13)
pdf.SetTextColor(0, 0, 0)
pdf.MultiCell(valueW, lineH, value, "", "L", false)
// 将光标移动到下一行开头,恢复默认颜色
pdf.SetDrawColor(0, 0, 0)
pdf.SetFillColor(255, 255, 255)
pdf.SetXY(lMargin, y+rowH)
}
func safePlaceholder(s string) string {
if s == "" {
return "-"
}
return s
}
// -------------------------
// 各板块渲染(与 qiye.html 对齐)
// -------------------------
// 风险综合分析(只做风险点一览,与 qiye.html 中 renderRiskOverview 对齐的精简版)
func renderPDFRiskOverview(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
itemsRaw, ok := v["items"].([]interface{})
if !ok || len(itemsRaw) == 0 {
pdf.MultiCell(0, 6, "暂无风险分析数据", "", "L", false)
return
}
for _, it := range itemsRaw {
m, ok := it.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
continue
}
hit := false
if hv, ok := m["hit"]; ok {
if b, ok := hv.(bool); ok {
hit = b
} else {
hit = fmt.Sprint(hv) == "true"
}
}
status := "未命中"
if hit {
status = "命中"
}
pdf.MultiCell(0, 6, fmt.Sprintf("· %s%s", name, status), "", "L", false)
}
}
// 主体概览,对照 qiye.html renderBasic 中的字段顺序,做精简单列展示
func renderPDFBasic(pdf *gofpdf.Fpdf, fontName, title string, basic map[string]interface{}, entName, creditCode string) {
if len(basic) == 0 && entName == "" && creditCode == "" {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 13)
// 基本信息采用条目式行展示,隔行背景色,不使用表格框
rowIndex := 0
writeBasicRow := func(label, val string) {
if val == "" {
return
}
alt := rowIndex%2 == 0
pdfWriteBasicRow(pdf, fontName, label, val, alt)
rowIndex++
}
writeBasicRow("企业名称", entName)
writeBasicRow("统一社会信用代码", safePlaceholder(creditCode))
keys := []string{
"regNo", "orgCode", "entType", "entTypeCode", "entityTypeCode",
"establishDate", "registeredCapital", "regCap", "regCapCurrency",
"regCapCurrencyCode", "regOrg", "regOrgCode", "regProvince", "provinceCode",
"regCity", "regCityCode", "regDistrict", "districtCode", "address",
"postalCode", "legalRepresentative", "compositionForm", "approvedBusinessItem",
"status", "statusCode", "operationPeriodFrom", "operationPeriodTo", "approveDate",
"cancelDate", "revokeDate", "cancelReason", "revokeReason",
"oldNames", "businessScope", "lastAnnuReportYear",
}
for _, k := range keys {
val := getString(basic, k)
if k == "oldNames" && val == "" {
continue
}
if val == "" {
continue
}
writeBasicRow(mapBasicLabel(k), val)
}
}
func mapBasicLabel(k string) string {
switch k {
case "regNo":
return "注册号"
case "orgCode":
return "组织机构代码"
case "entType":
return "企业类型"
case "entTypeCode":
return "企业类型编码"
case "entityTypeCode":
return "实体类型编码"
case "establishDate":
return "成立日期"
case "registeredCapital", "regCap":
return "注册资本"
case "regCapCurrency":
return "注册资本币种"
case "regCapCurrencyCode":
return "注册资本币种代码"
case "regOrg":
return "登记机关"
case "regOrgCode":
return "注册地址行政编号"
case "regProvince":
return "所在省份"
case "provinceCode":
return "所在省份编码"
case "regCity":
return "所在城市"
case "regCityCode":
return "所在城市编码"
case "regDistrict":
return "所在区/县"
case "districtCode":
return "所在区/县编码"
case "address":
return "住址"
case "postalCode":
return "邮编"
case "legalRepresentative":
return "法定代表人"
case "compositionForm":
return "组成形式"
case "approvedBusinessItem":
return "许可经营项目"
case "status":
return "经营状态"
case "statusCode":
return "经营状态编码"
case "operationPeriodFrom":
return "经营期限自"
case "operationPeriodTo":
return "经营期限至"
case "approveDate":
return "核准日期"
case "cancelDate":
return "注销日期"
case "revokeDate":
return "吊销日期"
case "cancelReason":
return "注销原因"
case "revokeReason":
return "吊销原因"
case "oldNames":
return "曾用名"
case "businessScope":
return "经营业务范围"
case "lastAnnuReportYear":
return "最后年检年度"
default:
return k
}
}
func renderPDFBranches(pdf *gofpdf.Fpdf, fontName, title string, branches []interface{}) {
if len(branches) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
for i, b := range branches {
m, ok := b.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
name = getString(m, "entName")
}
if name == "" {
continue
}
regNo := getString(m, "regNo")
credit := getString(m, "creditCode")
regOrg := getString(m, "regOrg")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, name), "", "L", false)
meta := fmt.Sprintf(" 注册号:%s信用代码%s登记机关%s",
safePlaceholder(regNo),
safePlaceholder(credit),
safePlaceholder(regOrg),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
func renderPDFShareholding(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
// 汇总(对齐前端 renderShareholding 的 summary 部分)
shareholderCount := getString(v, "shareholderCount")
registeredCapital := getString(v, "registeredCapital")
currency := getString(v, "currency")
topHolderName := getString(v, "topHolderName")
topHolderPercent := getString(v, "topHolderPercent")
top5TotalPercent := getString(v, "top5TotalPercent")
hasEquityPledges := boolToCN(getString(v, "hasEquityPledges"))
if shareholderCount != "" || registeredCapital != "" || currency != "" ||
topHolderName != "" || topHolderPercent != "" || top5TotalPercent != "" || hasEquityPledges != "" {
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "股权汇总", "", "L", false)
pdf.SetFont(fontName, "", 11)
if shareholderCount != "" {
writeKeyValue(pdf, fontName, "股东总数", shareholderCount)
}
if registeredCapital != "" {
writeKeyValue(pdf, fontName, "注册资本", registeredCapital+" 元")
}
if currency != "" {
writeKeyValue(pdf, fontName, "币种", currency)
}
if topHolderName != "" {
writeKeyValue(pdf, fontName, "第一大股东", topHolderName)
}
if topHolderPercent != "" {
writeKeyValue(pdf, fontName, "第一大股东持股", topHolderPercent)
}
if top5TotalPercent != "" {
writeKeyValue(pdf, fontName, "前五大合计", top5TotalPercent)
}
if hasEquityPledges != "" {
writeKeyValue(pdf, fontName, "存在股权出质", hasEquityPledges)
}
}
// 股东明细(股东全量字段的精简版)
if shRaw, ok := v["shareholders"].([]interface{}); ok && len(shRaw) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "股东明细", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, sh := range shRaw {
m, ok := sh.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
continue
}
pct := getString(m, "ownershipPercent")
if pct == "" {
pct = getString(m, "sharePercent")
}
titleLine := fmt.Sprintf("%d. %s", i+1, name)
if pct != "" {
titleLine = fmt.Sprintf("%s持股%s", titleLine, pct)
}
pdf.MultiCell(0, 6, titleLine, "", "L", false)
// 认缴 / 实缴等关键信息
subscribedAmount := getString(m, "subscribedAmount")
subscribedCurrency := getString(m, "subscribedCurrency")
subscribedCurrencyCode := getString(m, "subscribedCurrencyCode")
subscribedDate := getString(m, "subscribedDate")
subscribedMethod := getString(m, "subscribedMethod")
subscribedMethodCode := getString(m, "subscribedMethodCode")
paidAmount := getString(m, "paidAmount")
paidDate := getString(m, "paidDate")
paidMethod := getString(m, "paidMethod")
creditCode := getString(m, "creditCode")
regNo := getString(m, "regNo")
isHistory := boolToCN(getString(m, "isHistory"))
metaParts := []string{}
if subscribedAmount != "" {
sub := "认缴:" + subscribedAmount
if subscribedCurrency != "" || subscribedCurrencyCode != "" {
sub += " " + subscribedCurrency
if subscribedCurrencyCode != "" {
sub += "" + subscribedCurrencyCode + ""
}
}
metaParts = append(metaParts, sub)
}
if subscribedDate != "" {
metaParts = append(metaParts, "认缴日:"+subscribedDate)
}
if subscribedMethod != "" || subscribedMethodCode != "" {
subm := "认缴方式:" + subscribedMethod
if subscribedMethodCode != "" {
subm += "" + subscribedMethodCode + ""
}
metaParts = append(metaParts, subm)
}
if paidAmount != "" {
metaParts = append(metaParts, "实缴:"+paidAmount)
}
if paidDate != "" {
metaParts = append(metaParts, "实缴日:"+paidDate)
}
if paidMethod != "" {
metaParts = append(metaParts, "实缴方式:"+paidMethod)
}
if creditCode != "" {
metaParts = append(metaParts, "股东信用代码:"+creditCode)
}
if regNo != "" {
metaParts = append(metaParts, "股东注册号:"+regNo)
}
if isHistory != "" {
metaParts = append(metaParts, "是否历史:"+isHistory)
}
if len(metaParts) > 0 {
pdf.MultiCell(0, 5, " "+joinWithChineseSemicolon(metaParts), "", "L", false)
}
pdf.Ln(1)
}
}
// 股权变更记录
if changes, ok := v["equityChanges"].([]interface{}); ok && len(changes) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "股权变更记录", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, e := range changes {
m, ok := e.(map[string]interface{})
if !ok {
continue
}
changeDate := getString(m, "changeDate")
shareholderName := getString(m, "shareholderName")
titleLine := fmt.Sprintf("%d. %s %s", i+1, safePlaceholder(changeDate), shareholderName)
pdf.MultiCell(0, 6, titleLine, "", "L", false)
before := getString(m, "percentBefore")
after := getString(m, "percentAfter")
if before != "" || after != "" {
pdf.MultiCell(0, 5, fmt.Sprintf(" 变更前:%s → 变更后:%s", safePlaceholder(before), safePlaceholder(after)), "", "L", false)
}
pdf.Ln(1)
}
}
// 股权出质
if pledges, ok := v["equityPledges"].([]interface{}); ok && len(pledges) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "股权出质", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, e := range pledges {
m, ok := e.(map[string]interface{})
if !ok {
continue
}
regNo := getString(m, "regNo")
pledgor := getString(m, "pledgor")
pledgee := getString(m, "pledgee")
titleLine := fmt.Sprintf("%d. %s %s → %s", i+1, safePlaceholder(regNo), safePlaceholder(pledgor), safePlaceholder(pledgee))
pdf.MultiCell(0, 6, titleLine, "", "L", false)
amount := getString(m, "pledgedAmount")
regDate := getString(m, "regDate")
status := getString(m, "status")
meta := fmt.Sprintf(" 出质数额:%s 元;登记日:%s状态%s",
safePlaceholder(amount),
safePlaceholder(regDate),
safePlaceholder(status),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 实缴出资明细
if paidDetails, ok := v["paidInDetails"].([]interface{}); ok && len(paidDetails) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "实缴出资明细", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, e := range paidDetails {
m, ok := e.(map[string]interface{})
if !ok {
continue
}
investor := getString(m, "investor")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(investor)), "", "L", false)
paidDate := getString(m, "paidDate")
paidMethod := getString(m, "paidMethod")
accumulatedPaid := getString(m, "accumulatedPaid")
meta := fmt.Sprintf(" 日期:%s方式%s累计实缴%s 万元",
safePlaceholder(paidDate),
safePlaceholder(paidMethod),
safePlaceholder(accumulatedPaid),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 认缴出资明细
if subDetails, ok := v["subscribedCapitalDetails"].([]interface{}); ok && len(subDetails) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "认缴出资明细", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, e := range subDetails {
m, ok := e.(map[string]interface{})
if !ok {
continue
}
investor := getString(m, "investor")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(investor)), "", "L", false)
subscribedDate := getString(m, "subscribedDate")
subscribedMethod := getString(m, "subscribedMethod")
accSubscribed := getString(m, "accumulatedSubscribed")
meta := fmt.Sprintf(" 认缴日:%s方式%s累计认缴%s 元",
safePlaceholder(subscribedDate),
safePlaceholder(subscribedMethod),
safePlaceholder(accSubscribed),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
}
func renderPDFController(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
writeKeyValue(pdf, fontName, "标识", getString(v, "id"))
writeKeyValue(pdf, fontName, "姓名/名称", getString(v, "name"))
writeKeyValue(pdf, fontName, "类型", getString(v, "type"))
writeKeyValue(pdf, fontName, "持股比例", getString(v, "percent"))
writeKeyValue(pdf, fontName, "判定依据", getString(v, "reason"))
writeKeyValue(pdf, fontName, "来源", getString(v, "source"))
}
func renderPDFBeneficiaries(pdf *gofpdf.Fpdf, fontName, title string, arr []interface{}) {
if len(arr) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
for i, b := range arr {
m, ok := b.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
continue
}
pct := getString(m, "percent")
line := fmt.Sprintf("%d. %s", i+1, name)
if pct != "" {
line = fmt.Sprintf("%s持股%s", line, pct)
}
pdf.MultiCell(0, 6, line, "", "L", false)
reason := getString(m, "reason")
if reason != "" {
pdf.MultiCell(0, 5, fmt.Sprintf(" 原因:%s", reason), "", "L", false)
}
pdf.Ln(1)
}
}
func renderPDFInvestments(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
// 汇总
totalCount := getString(v, "totalCount")
totalAmount := getString(v, "totalAmount")
if totalCount != "" || totalAmount != "" {
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "汇总", "", "L", false)
pdf.SetFont(fontName, "", 11)
if totalCount != "" {
writeKeyValue(pdf, fontName, "对外投资数量", totalCount)
}
if totalAmount != "" {
writeKeyValue(pdf, fontName, "投资总额(万)", totalAmount)
}
}
// 对外投资列表
list := []interface{}{}
if arr, ok := v["list"].([]interface{}); ok {
list = arr
} else if arr, ok := v["entities"].([]interface{}); ok {
list = arr
}
if len(list) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "对外投资列表", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, inv := range list {
m, ok := inv.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "entName")
if name == "" {
name = getString(m, "name")
}
if name == "" {
continue
}
pct := getString(m, "investPercent")
titleLine := fmt.Sprintf("%d. %s", i+1, name)
if pct != "" {
titleLine = fmt.Sprintf("%s持股%s", titleLine, pct)
}
pdf.MultiCell(0, 6, titleLine, "", "L", false)
}
}
}
func renderPDFGuarantees(pdf *gofpdf.Fpdf, fontName, title string, arr []interface{}) {
if len(arr) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
for i, it := range arr {
m, ok := it.(map[string]interface{})
if !ok {
continue
}
mortgagor := getString(m, "mortgagor")
creditor := getString(m, "creditor")
if mortgagor == "" && creditor == "" {
continue
}
titleLine := fmt.Sprintf("%d. %s → %s", i+1, safePlaceholder(mortgagor), safePlaceholder(creditor))
pdf.MultiCell(0, 6, titleLine, "", "L", false)
amount := getString(m, "principalAmount")
kind := getString(m, "principalKind")
guaranteeType := getString(m, "guaranteeType")
periodFrom := getString(m, "periodFrom")
periodTo := getString(m, "periodTo")
meta := fmt.Sprintf(" 主债权数额:%s种类%s保证方式%s期限%s 至 %s",
safePlaceholder(amount),
safePlaceholder(kind),
safePlaceholder(guaranteeType),
safePlaceholder(periodFrom),
safePlaceholder(periodTo),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
func renderPDFManagement(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
// 高管列表
if execs, ok := v["executives"].([]interface{}); ok && len(execs) > 0 {
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "高管列表", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, e := range execs {
m, ok := e.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
continue
}
position := getString(m, "position")
line := fmt.Sprintf("%d. %s", i+1, name)
if position != "" {
line = fmt.Sprintf("%s%s", line, position)
}
pdf.MultiCell(0, 6, line, "", "L", false)
}
}
// 从业与社保
employeeCount := getString(v, "employeeCount")
femaleEmployeeCount := getString(v, "femaleEmployeeCount")
if employeeCount != "" || femaleEmployeeCount != "" {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "从业与社保", "", "L", false)
pdf.SetFont(fontName, "", 11)
if employeeCount != "" {
writeKeyValue(pdf, fontName, "从业人数", employeeCount)
}
if femaleEmployeeCount != "" {
writeKeyValue(pdf, fontName, "女性从业人数", femaleEmployeeCount)
}
}
// 法定代表人其他任职
var others []interface{}
if arr, ok := v["legalRepresentativeOtherPositions"].([]interface{}); ok {
others = arr
} else if arr, ok := v["legalPersonOtherPositions"].([]interface{}); ok {
others = arr
}
if len(others) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "法定代表人其他任职", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, f := range others {
m, ok := f.(map[string]interface{})
if !ok {
continue
}
entName := getString(m, "entName")
if entName == "" {
continue
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, entName), "", "L", false)
parts := []string{}
if pos := getString(m, "position"); pos != "" {
parts = append(parts, "职务:"+pos)
}
if name := getString(m, "name"); name != "" {
parts = append(parts, "姓名:"+name)
}
if st := getString(m, "entStatus"); st != "" {
parts = append(parts, "企业状态:"+st)
}
if cc := getString(m, "creditCode"); cc != "" {
parts = append(parts, "信用代码:"+cc)
}
if rn := getString(m, "regNo"); rn != "" {
parts = append(parts, "注册号:"+rn)
}
if len(parts) > 0 {
pdf.MultiCell(0, 5, " "+joinWithChineseSemicolon(parts), "", "L", false)
}
pdf.Ln(1)
}
}
}
func renderPDFAssets(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
yearsRaw, ok := v["years"].([]interface{})
if !ok || len(yearsRaw) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
for _, y := range yearsRaw {
m, ok := y.(map[string]interface{})
if !ok {
continue
}
year := getString(m, "year")
reportDate := getString(m, "reportDate")
titleLine := year
if reportDate != "" {
titleLine = fmt.Sprintf("%s%s", year, reportDate)
}
pdf.MultiCell(0, 6, titleLine, "", "L", false)
meta := fmt.Sprintf(" 资产总额:%s营收%s净利润%s负债%s",
safePlaceholder(getString(m, "assetTotal")),
safePlaceholder(getString(m, "revenueTotal")),
safePlaceholder(getString(m, "netProfit")),
safePlaceholder(getString(m, "liabilityTotal")),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
func renderPDFLicenses(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
// 行政许可列表
if permits, ok := v["permits"].([]interface{}); ok && len(permits) > 0 {
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "行政许可列表", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, p := range permits {
m, ok := p.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
continue
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, name), "", "L", false)
meta := fmt.Sprintf(" 有效期:%s ~ %s许可机关%s%s",
safePlaceholder(getString(m, "valFrom")),
safePlaceholder(getString(m, "valTo")),
safePlaceholder(getString(m, "licAnth")),
safePlaceholder(getString(m, "licItem")),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 许可变更记录
if changes, ok := v["permitChanges"].([]interface{}); ok && len(changes) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "许可变更记录", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, p := range changes {
m, ok := p.(map[string]interface{})
if !ok {
continue
}
changeDate := getString(m, "changeDate")
changeType := getString(m, "changeType")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s %s", i+1, safePlaceholder(changeDate), safePlaceholder(changeType)), "", "L", false)
before := getString(m, "detailBefore")
after := getString(m, "detailAfter")
if before != "" || after != "" {
pdf.MultiCell(0, 5, " 变更前:"+before, "", "L", false)
pdf.MultiCell(0, 5, " 变更后:"+after, "", "L", false)
}
pdf.Ln(1)
}
}
// 知识产权出质(如有则原样列出)
if ipPledges, ok := v["ipPledges"].([]interface{}); ok && len(ipPledges) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "知识产权出质", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, it := range ipPledges {
pdf.MultiCell(0, 5, fmt.Sprintf("%d. %v", i+1, it), "", "L", false)
}
}
}
func renderPDFActivities(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
// 招投标
if bids, ok := v["bids"].([]interface{}); ok && len(bids) > 0 {
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "招投标", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, b := range bids {
m, ok := b.(map[string]interface{})
if !ok {
continue
}
titleText := getString(m, "announcetitle")
if titleText == "" {
titleText = getString(m, "ANNOUNCETITLE")
}
if titleText == "" {
titleText = getString(m, "title")
}
if titleText == "" {
continue
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, titleText), "", "L", false)
}
}
// 网站 / 网店
if websites, ok := v["websites"].([]interface{}); ok && len(websites) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "网站 / 网店", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, w := range websites {
m, ok := w.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "websitname")
if name == "" {
name = getString(m, "WEBSITNAME")
}
if name == "" {
name = getString(m, "name")
}
if name == "" {
continue
}
webType := getString(m, "webtype")
if webType == "" {
webType = getString(m, "WEBTYPE")
}
domain := getString(m, "domain")
if domain == "" {
domain = getString(m, "DOMAIN")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, name), "", "L", false)
meta := fmt.Sprintf(" 类型:%s域名%s", safePlaceholder(webType), safePlaceholder(domain))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
}
func renderPDFInspections(pdf *gofpdf.Fpdf, fontName, title string, arr []interface{}) {
if len(arr) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
for i, it := range arr {
m, ok := it.(map[string]interface{})
if !ok {
continue
}
date := getString(m, "inspectDate")
result := getString(m, "result")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s %s", i+1, safePlaceholder(date), safePlaceholder(result)), "", "L", false)
}
}
func renderPDFRisks(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
// has* 风险项一览(有 / 无)
type item struct {
Key string
Label string
}
items := []item{
{"hasCourtJudgments", "裁判文书"},
{"hasJudicialAssists", "司法协助"},
{"hasDishonestDebtors", "失信被执行人"},
{"hasLimitHighDebtors", "限高被执行人"},
{"hasAdminPenalty", "行政处罚"},
{"hasException", "经营异常"},
{"hasSeriousIllegal", "严重违法"},
{"hasTaxOwing", "欠税"},
{"hasSeriousTaxIllegal", "重大税收违法"},
{"hasMortgage", "动产抵押"},
{"hasEquityPledges", "股权出质"},
{"hasQuickCancel", "简易注销"},
}
for _, it := range items {
val := getString(v, it.Key)
status := "未发现"
if val == "true" || val == "1" {
status = "存在"
}
pdf.MultiCell(0, 6, fmt.Sprintf("· %s%s", it.Label, status), "", "L", false)
}
// 裁判文书
if arr, ok := v["courtJudgments"].([]interface{}); !ok || len(arr) == 0 {
// 兼容 judicialCases 字段
if alt, ok2 := v["judicialCases"].([]interface{}); ok2 {
arr = alt
ok = len(arr) > 0
}
}
if arr, ok := v["courtJudgments"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "裁判文书", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, c := range arr {
m, ok := c.(map[string]interface{})
if !ok {
continue
}
caseNo := getString(m, "caseNumber")
if caseNo == "" {
caseNo = getString(m, "CASENUMBER")
}
if caseNo == "" {
caseNo = getString(m, "judicialDocumentId")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. 案号:%s", i+1, safePlaceholder(caseNo)), "", "L", false)
}
}
// 司法协助
if arr, ok := v["judicialAssists"].([]interface{}); !ok || len(arr) == 0 {
if alt, ok2 := v["judicialAids"].([]interface{}); ok2 {
arr = alt
ok = len(arr) > 0
}
if ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "司法协助", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, a := range arr {
m, ok := a.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "iname")
if name == "" {
name = getString(m, "INAME")
}
if name == "" {
name = getString(m, "marketName")
}
court := getString(m, "courtName")
if court == "" {
court = getString(m, "COURTNAME")
}
share := getString(m, "shaream")
if share == "" {
share = getString(m, "SHAREAM")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(name)), "", "L", false)
meta := fmt.Sprintf(" 法院:%s股权数额%s 元", safePlaceholder(court), safePlaceholder(share))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
}
// 失信被执行人
if arr, ok := v["dishonestDebtors"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "失信被执行人", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, d := range arr {
m, ok := d.(map[string]interface{})
if !ok {
continue
}
caseNo := getString(m, "caseNo")
execCourt := getString(m, "execCourt")
performance := getString(m, "performanceStatus")
publishDate := getString(m, "publishDate")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. 案号 %s", i+1, safePlaceholder(caseNo)), "", "L", false)
meta := fmt.Sprintf(" 执行法院:%s履行情况%s发布时间%s",
safePlaceholder(execCourt),
safePlaceholder(performance),
safePlaceholder(publishDate),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 限高被执行人
if arr, ok := v["limitHighDebtors"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "限高被执行人", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, x := range arr {
m, ok := x.(map[string]interface{})
if !ok {
continue
}
ah := getString(m, "ah")
if ah == "" {
ah = getString(m, "caseNo")
}
zxfy := getString(m, "zxfy")
if zxfy == "" {
zxfy = getString(m, "execCourt")
}
fbrq := getString(m, "fbrq")
if fbrq == "" {
fbrq = getString(m, "publishDate")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(ah)), "", "L", false)
meta := fmt.Sprintf(" 执行法院:%s发布日期%s", safePlaceholder(zxfy), safePlaceholder(fbrq))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 行政处罚
if arr, ok := v["adminPenalties"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "行政处罚", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, p := range arr {
m, ok := p.(map[string]interface{})
if !ok {
continue
}
pendecno := getString(m, "pendecno")
if pendecno == "" {
pendecno = getString(m, "PENDECNO")
}
illeg := getString(m, "illegacttype")
if illeg == "" {
illeg = getString(m, "ILLEGACTTYPE")
}
auth := getString(m, "penauth")
if auth == "" {
auth = getString(m, "PENAUTH")
}
date := getString(m, "pendecissdate")
if date == "" {
date = getString(m, "PENDECISSDATE")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(pendecno)), "", "L", false)
meta := fmt.Sprintf(" 违法类型:%s机关%s日期%s", safePlaceholder(illeg), safePlaceholder(auth), safePlaceholder(date))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 行政处罚变更
if arr, ok := v["adminPenaltyUpdates"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "行政处罚变更记录", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, p := range arr {
m, ok := p.(map[string]interface{})
if !ok {
continue
}
date := getString(m, "updateDate")
content := getString(m, "updateContent")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(date)), "", "L", false)
if content != "" {
pdf.MultiCell(0, 5, " "+content, "", "L", false)
}
pdf.Ln(1)
}
}
// 经营异常
if arr, ok := v["exceptions"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "经营异常", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, e := range arr {
m, ok := e.(map[string]interface{})
if !ok {
continue
}
indate := getString(m, "indate")
if indate == "" {
indate = getString(m, "INDATE")
}
inreason := getString(m, "inreason")
if inreason == "" {
inreason = getString(m, "INREASON")
}
outdate := getString(m, "outdate")
if outdate == "" {
outdate = getString(m, "OUTDATE")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(indate)), "", "L", false)
meta := fmt.Sprintf(" 原因:%s移出%s", safePlaceholder(inreason), safePlaceholder(outdate))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 动产抵押
if arr, ok := v["mortgages"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "动产抵押", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, m := range arr {
mm, ok := m.(map[string]interface{})
if !ok {
continue
}
regNo := getString(mm, "regNo")
amount := getString(mm, "guaranteedAmount")
regDate := getString(mm, "regDate")
regOrg := getString(mm, "regOrg")
status := getString(mm, "status")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s %s", i+1, safePlaceholder(regNo), safePlaceholder(amount)), "", "L", false)
meta := fmt.Sprintf(" 登记日:%s机关%s状态%s", safePlaceholder(regDate), safePlaceholder(regOrg), safePlaceholder(status))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
// 简易注销
if qc, ok := v["quickCancel"].(map[string]interface{}); ok && len(qc) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "简易注销", "", "L", false)
pdf.SetFont(fontName, "", 11)
writeKeyValue(pdf, fontName, "企业名称", getString(qc, "entName"))
writeKeyValue(pdf, fontName, "统一社会信用代码", getString(qc, "creditCode"))
writeKeyValue(pdf, fontName, "注册号", getString(qc, "regNo"))
writeKeyValue(pdf, fontName, "登记机关", getString(qc, "regOrg"))
writeKeyValue(pdf, fontName, "公告起始日期", getString(qc, "noticeFromDate"))
writeKeyValue(pdf, fontName, "公告结束日期", getString(qc, "noticeToDate"))
writeKeyValue(pdf, fontName, "注销结果", getString(qc, "cancelResult"))
}
// 清算信息
if liq, ok := v["liquidation"].(map[string]interface{}); ok && len(liq) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "清算信息", "", "L", false)
pdf.SetFont(fontName, "", 11)
writeKeyValue(pdf, fontName, "负责人", getString(liq, "principal"))
writeKeyValue(pdf, fontName, "成员", getString(liq, "members"))
}
// 纳税与欠税taxRecords
if tax, ok := v["taxRecords"].(map[string]interface{}); ok && len(tax) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "纳税与欠税", "", "L", false)
pdf.SetFont(fontName, "", 11)
ayears := getSlice(tax, "taxLevelAYears")
owings := getSlice(tax, "taxOwings")
hint := fmt.Sprintf("纳税A级年度%s欠税%s",
formatCountHint(len(ayears)),
formatCountHint(len(owings)),
)
pdf.MultiCell(0, 5, hint, "", "L", false)
if len(owings) > 0 {
pdf.Ln(1)
for i, t := range owings {
m, ok := t.(map[string]interface{})
if !ok {
continue
}
taxType := getString(m, "taxOwedType")
if taxType == "" {
taxType = getString(m, "taxowedtype")
}
total := getString(m, "totalOwedAmount")
if total == "" {
total = getString(m, "totalowedamount")
}
pub := getString(m, "publishDate")
if pub == "" {
pub = getString(m, "publishdate")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(taxType)), "", "L", false)
meta := fmt.Sprintf(" 合计:%s公示日%s", safePlaceholder(total), safePlaceholder(pub))
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
} else {
// 完全无 taxRecords 时,与 HTML 行为一致给一个“无”的提示
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "纳税与欠税", "", "L", false)
pdf.SetFont(fontName, "", 11)
pdf.MultiCell(0, 5, "无", "", "L", false)
}
// 涉诉案件litigation—— 按案件类型分类
if lit, ok := v["litigation"].(map[string]interface{}); ok && len(lit) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "涉诉案件汇总", "", "L", false)
pdf.SetFont(fontName, "", 11)
typeLabels := map[string]string{
"administrative": "行政案件",
"implement": "执行案件",
"preservation": "非诉保全审查",
"civil": "民事案件",
"criminal": "刑事案件",
"bankrupt": "强制清算与破产案件",
"jurisdict": "管辖案件",
"compensate": "赔偿案件",
}
// 汇总表
for key, label := range typeLabels {
if sec, ok := lit[key].(map[string]interface{}); ok {
count := getString(sec, "count")
if count != "" && count != "0" {
pdf.MultiCell(0, 5, fmt.Sprintf("· %s%s 件", label, count), "", "L", false)
}
}
}
// 各类案件明细
for key, label := range typeLabels {
sec, ok := lit[key].(map[string]interface{})
if !ok {
continue
}
cases, ok := sec["cases"].([]interface{})
if !ok || len(cases) == 0 {
continue
}
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, label, "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, c := range cases {
m, ok := c.(map[string]interface{})
if !ok {
continue
}
caseNo := getString(m, "caseNo")
court := getString(m, "court")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s%s", i+1, safePlaceholder(caseNo), safePlaceholder(court)), "", "L", false)
region := getString(m, "region")
trialLevel := getString(m, "trialLevel")
caseType := getString(m, "caseType")
cause := getString(m, "cause")
amount := getString(m, "amount")
filing := getString(m, "filingDate")
judgment := getString(m, "judgmentDate")
victory := getString(m, "victoryResult")
meta := fmt.Sprintf(" 地区:%s审级%s案件类型%s案由%s标的金额%s立案日期%s裁判日期%s胜败结果%s",
safePlaceholder(region),
safePlaceholder(trialLevel),
safePlaceholder(caseType),
safePlaceholder(cause),
safePlaceholder(amount),
safePlaceholder(filing),
safePlaceholder(judgment),
safePlaceholder(victory),
)
pdf.MultiCell(0, 5, meta, "", "L", false)
pdf.Ln(1)
}
}
}
}
func renderPDFTimeline(pdf *gofpdf.Fpdf, fontName, title string, arr []interface{}) {
if len(arr) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
for i, it := range arr {
m, ok := it.(map[string]interface{})
if !ok {
continue
}
date := getString(m, "date")
tp := getString(m, "type")
desc := getString(m, "title")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s %s", i+1, safePlaceholder(date), safePlaceholder(tp)), "", "L", false)
if desc != "" {
pdf.MultiCell(0, 5, " "+desc, "", "L", false)
}
pdf.Ln(1)
}
}
func renderPDFListed(pdf *gofpdf.Fpdf, fontName, title string, v map[string]interface{}) {
if len(v) == 0 {
return
}
pdf.AddPage()
pdfSectionTitle(pdf, fontName, title)
pdf.Ln(1)
pdf.SetFont(fontName, "", 11)
isListed := boolToCN(getString(v, "isListed"))
if isListed != "" {
writeKeyValue(pdf, fontName, "是否上市", isListed)
}
// 上市公司信息
if company, ok := v["company"].(map[string]interface{}); ok && len(company) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "上市公司信息", "", "L", false)
pdf.SetFont(fontName, "", 11)
writeKeyValue(pdf, fontName, "经营范围", getString(company, "bizScope"))
writeKeyValue(pdf, fontName, "信用代码", getString(company, "creditCode"))
writeKeyValue(pdf, fontName, "注册地址", getString(company, "regAddr"))
writeKeyValue(pdf, fontName, "注册资本", getString(company, "regCapital"))
writeKeyValue(pdf, fontName, "组织机构代码", getString(company, "orgCode"))
writeKeyValue(pdf, fontName, "币种", getString(company, "cur"))
writeKeyValue(pdf, fontName, "币种名称", getString(company, "curName"))
}
// 股票信息
if stock, ok := v["stock"]; ok && stock != nil {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "股票信息", "", "L", false)
pdf.SetFont(fontName, "", 11)
switch s := stock.(type) {
case string:
if s != "" {
pdf.MultiCell(0, 5, s, "", "L", false)
}
case map[string]interface{}:
// 简单将几个常见字段以表格形式展示
code := getString(s, "code")
name := getString(s, "name")
market := getString(s, "market")
if code != "" {
writeKeyValue(pdf, fontName, "代码", code)
}
if name != "" {
writeKeyValue(pdf, fontName, "名称", name)
}
if market != "" {
writeKeyValue(pdf, fontName, "市场", market)
}
default:
// 其他类型非空时,用字符串方式输出
txt := fmt.Sprint(stock)
if txt != "" && txt != "<nil>" {
pdf.MultiCell(0, 5, txt, "", "L", false)
}
}
}
// 十大股东
if arr, ok := v["topShareholders"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "十大股东", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, s := range arr {
m, ok := s.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
name = getString(m, "shaname")
}
pct := getString(m, "percent")
if pct == "" {
pct = getString(m, "fundedratio")
}
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(name)), "", "L", false)
if pct != "" {
pdf.MultiCell(0, 5, " 持股比例:"+pct, "", "L", false)
}
pdf.Ln(1)
}
}
// 上市高管
if arr, ok := v["listedManagers"].([]interface{}); ok && len(arr) > 0 {
pdf.Ln(2)
pdf.SetFont(fontName, "B", 12)
pdf.MultiCell(0, 6, "上市高管", "", "L", false)
pdf.SetFont(fontName, "", 11)
for i, mng := range arr {
m, ok := mng.(map[string]interface{})
if !ok {
continue
}
name := getString(m, "name")
if name == "" {
name = getString(m, "perName")
}
position := getString(m, "position")
pdf.MultiCell(0, 6, fmt.Sprintf("%d. %s", i+1, safePlaceholder(name)), "", "L", false)
if position != "" {
pdf.MultiCell(0, 5, " 职务:"+position, "", "L", false)
}
pdf.Ln(1)
}
}
}
// joinWithChineseSemicolon 使用中文分号连接字符串
func joinWithChineseSemicolon(parts []string) string {
if len(parts) == 0 {
return ""
}
res := parts[0]
for i := 1; i < len(parts); i++ {
res += "" + parts[i]
}
return res
}
// pdfSectionTitle 渲染模块主标题(对应 HTML h2
func pdfSectionTitle(pdf *gofpdf.Fpdf, fontName, title string) {
pdf.SetFont(fontName, "B", 17)
// 模块标题前加蓝色点缀方块
pdf.SetTextColor(37, 99, 235)
pdf.CellFormat(0, 10, "■ "+title, "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
}
// pdfSubTitle 渲染模块内小节标题(对应 HTML h3
func pdfSubTitle(pdf *gofpdf.Fpdf, fontName, title string) {
pdf.SetFont(fontName, "B", 14)
// 小节标题用圆点前缀,略微缩进
pdf.SetTextColor(37, 99, 235)
pdf.CellFormat(0, 8, "● "+title, "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
}
// pdfWriteBasicRow 渲染基础信息的一行,使用隔行背景色而不加表格框
func pdfWriteBasicRow(pdf *gofpdf.Fpdf, fontName, label, value string, alt bool) {
if value == "" {
return
}
pageW, pageH := pdf.GetPageSize()
lMargin, _, rMargin, bMargin := pdf.GetMargins()
x := lMargin
y := pdf.GetY()
w := pageW - lMargin - rMargin
labelW := 40.0
valueW := w - labelW
// 行高整体拉宽一点
lineH := 10.0
// 预先根据内容拆行,计算本行整体高度(用于背景块)
lines := pdf.SplitLines([]byte(value), valueW)
rowH := float64(len(lines)) * lineH
if rowH < lineH {
rowH = lineH
}
rowH += 2
// 如当前页剩余空间不足,先分页再画整行,避免内容与下一页重叠
if y+rowH > pageH-bMargin {
pdf.AddPage()
y = pdf.GetY()
pdf.SetXY(lMargin, y)
}
// 隔行底色
if alt {
pdf.SetFillColor(242, 242, 242)
} else {
pdf.SetFillColor(255, 255, 255)
}
pdf.Rect(x, y, w, rowH, "F")
// 标签列
pdf.SetXY(x, y)
pdf.SetFont(fontName, "B", 14)
pdf.CellFormat(labelW, rowH, fmt.Sprintf("%s", label), "", 0, "L", false, 0, "")
// 值列自动换行
pdf.SetXY(x+labelW, y)
pdf.SetFont(fontName, "", 14)
pdf.MultiCell(valueW, lineH, value, "", "L", false)
// 移到下一行起始位置
pdf.SetXY(lMargin, y+rowH)
}
// formatCountHint 将条数格式化为“X 条”或“无”
func formatCountHint(n int) string {
if n <= 0 {
return "无"
}
return fmt.Sprintf("%d 条", n)
}
// boolToCN 将 true/false/1/0/yes/no 映射成 “是/否”
func boolToCN(s string) string {
if s == "" {
return s
}
low := strings.ToLower(strings.TrimSpace(s))
switch low {
case "true", "1", "yes", "y":
return "是"
case "false", "0", "no", "n":
return "否"
default:
return s
}
}