4e2cd43a55
iotlcm-onelink-mock:OneLink 挡板服务(本地测试替身)。 - Go module 迁移至 git.5iot.com/iotlcm-public/iotlcm-onelink-mock - 修复同毫秒内多次生成卡导致 ICCID/IMSI 唯一约束冲突 - 补 Apache-2.0 LICENSE,README 重写并新增 docs/deploy.md - 移除内部部署资产(CI 触发器 / 镜像推送脚本 / 指定环境部署文档) - 测试凭据改为 demo_appid / demo_password,真实手机号与内网 IP 脱敏 - 统一 LF 行尾并加 .gitattributes
162 lines
6.6 KiB
Go
162 lines
6.6 KiB
Go
package service
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func exportInboundCardsXLSX(cards []SIMCard) ([]byte, error) {
|
|
var buf bytes.Buffer
|
|
zw := zip.NewWriter(&buf)
|
|
files := map[string]string{
|
|
"[Content_Types].xml": contentTypesXML,
|
|
"_rels/.rels": rootRelsXML,
|
|
"xl/workbook.xml": workbookXML,
|
|
"xl/_rels/workbook.xml.rels": workbookRelsXML,
|
|
"xl/sharedStrings.xml": sharedStringsXML(),
|
|
"xl/styles.xml": stylesXML,
|
|
"xl/worksheets/sheet1.xml": sheetXML(cards),
|
|
}
|
|
for _, name := range []string{"[Content_Types].xml", "_rels/.rels", "xl/workbook.xml", "xl/_rels/workbook.xml.rels", "xl/sharedStrings.xml", "xl/styles.xml", "xl/worksheets/sheet1.xml"} {
|
|
w, err := zw.Create(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := w.Write([]byte(files[name])); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if err := zw.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func sheetXML(cards []SIMCard) string {
|
|
var b strings.Builder
|
|
b.WriteString(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`)
|
|
b.WriteString(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
|
b.WriteString(`<dimension ref="A1:R`)
|
|
b.WriteString(strconv.Itoa(len(cards) + 1))
|
|
b.WriteString(`"/><sheetViews><sheetView tabSelected="true" workbookViewId="0"/></sheetViews>`)
|
|
b.WriteString(`<sheetFormatPr defaultRowHeight="15"/><cols><col min="1" max="18" width="9.140625" style="1"/></cols><sheetData>`)
|
|
writeSharedStringRow(&b, 1, len(inboundExportHeaders))
|
|
for i, card := range cards {
|
|
row := inboundExportRow(card)
|
|
values := make([]string, 0, len(inboundExportColumns))
|
|
for _, column := range inboundExportColumns {
|
|
values = append(values, row[column.Key])
|
|
}
|
|
writeSheetRow(&b, i+2, values)
|
|
}
|
|
b.WriteString(`</sheetData></worksheet>`)
|
|
return b.String()
|
|
}
|
|
|
|
func writeSharedStringRow(b *strings.Builder, rowNum, count int) {
|
|
b.WriteString(`<row r="`)
|
|
b.WriteString(strconv.Itoa(rowNum))
|
|
b.WriteString(`">`)
|
|
for i := 0; i < count; i++ {
|
|
ref := fmt.Sprintf("%s%d", columnName(i+1), rowNum)
|
|
b.WriteString(`<c r="`)
|
|
b.WriteString(ref)
|
|
b.WriteString(`" s="1" t="s"><v>`)
|
|
b.WriteString(strconv.Itoa(i))
|
|
b.WriteString(`</v></c>`)
|
|
}
|
|
b.WriteString(`</row>`)
|
|
}
|
|
|
|
func writeSheetRow(b *strings.Builder, rowNum int, values []string) {
|
|
b.WriteString(`<row r="`)
|
|
b.WriteString(strconv.Itoa(rowNum))
|
|
b.WriteString(`">`)
|
|
for i, value := range values {
|
|
ref := fmt.Sprintf("%s%d", columnName(i+1), rowNum)
|
|
b.WriteString(`<c r="`)
|
|
b.WriteString(ref)
|
|
b.WriteString(`" s="1" t="inlineStr"><is><t>`)
|
|
b.WriteString(xmlEscape(value))
|
|
b.WriteString(`</t></is></c>`)
|
|
}
|
|
b.WriteString(`</row>`)
|
|
}
|
|
|
|
func sharedStringsXML() string {
|
|
var b strings.Builder
|
|
b.WriteString(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`)
|
|
b.WriteString(`<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="`)
|
|
b.WriteString(strconv.Itoa(len(inboundExportHeaders)))
|
|
b.WriteString(`" uniqueCount="`)
|
|
b.WriteString(strconv.Itoa(len(inboundExportHeaders)))
|
|
b.WriteString(`">`)
|
|
for _, header := range inboundExportHeaders {
|
|
b.WriteString(`<si><t>`)
|
|
b.WriteString(xmlEscape(header))
|
|
b.WriteString(`</t></si>`)
|
|
}
|
|
b.WriteString(`</sst>`)
|
|
return b.String()
|
|
}
|
|
|
|
func columnName(index int) string {
|
|
var out []byte
|
|
for index > 0 {
|
|
index--
|
|
out = append([]byte{byte('A' + index%26)}, out...)
|
|
index /= 26
|
|
}
|
|
return string(out)
|
|
}
|
|
|
|
func xmlEscape(value string) string {
|
|
var b strings.Builder
|
|
_ = xml.EscapeText(&b, []byte(value))
|
|
return b.String()
|
|
}
|
|
|
|
const contentTypesXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
<Default Extension="xml" ContentType="application/xml"/>
|
|
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
|
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
|
|
<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
|
|
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
|
|
</Types>`
|
|
|
|
const rootRelsXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
|
|
</Relationships>`
|
|
|
|
const workbookXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
<sheets>
|
|
<sheet name="Sheet1" sheetId="1" r:id="rId1"/>
|
|
</sheets>
|
|
</workbook>`
|
|
|
|
const workbookRelsXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
|
|
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
|
|
</Relationships>`
|
|
|
|
const stylesXML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
|
<fonts count="1"><font><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/></font></fonts>
|
|
<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>
|
|
<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>
|
|
<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>
|
|
<cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="49" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/></cellXfs>
|
|
<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>
|
|
<dxfs count="0"/><tableStyles count="0" defaultPivotStyle="PivotStyleLight16" defaultTableStyle="TableStyleMedium2"/>
|
|
</styleSheet>`
|