From d4a8f1cb34348a1f835820429105b3d443b6cbf1 Mon Sep 17 00:00:00 2001 From: dorlolo <428192774@qq.com> Date: Fri, 17 Mar 2023 15:17:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E5=AF=B9=E8=B4=A6?= =?UTF-8?q?=E5=8D=95pb=E6=96=87=E4=BB=B6=E4=B8=AD=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/internal/dao/artistinfo_statement.go | 14 +- pb/artistinfoStatement.proto | 32 +- .../artistinfoStatement.pb.go | 290 +++++++++--------- pkg/db/dto.go | 4 + pkg/util/excel/example/demo.xlsx | Bin 0 -> 9912 bytes pkg/util/excel/example/demo_1671435052.xlsx | Bin 0 -> 8445 bytes pkg/util/excel/example/exportExample_test.go | 70 +++++ pkg/util/excel/excelInter.go | 253 +++++++++++++++ pkg/util/excel/excel_test.go | 38 +++ pkg/util/excel/img.png | Bin 0 -> 6648 bytes pkg/util/excel/img_1.png | Bin 0 -> 26183 bytes pkg/util/excel/options.go | 27 ++ pkg/util/excel/readme.md | 161 ++++++++++ pkg/util/excel/utils.go | 41 +++ 14 files changed, 762 insertions(+), 168 deletions(-) create mode 100644 pkg/util/excel/example/demo.xlsx create mode 100644 pkg/util/excel/example/demo_1671435052.xlsx create mode 100644 pkg/util/excel/example/exportExample_test.go create mode 100644 pkg/util/excel/excelInter.go create mode 100644 pkg/util/excel/excel_test.go create mode 100644 pkg/util/excel/img.png create mode 100644 pkg/util/excel/img_1.png create mode 100644 pkg/util/excel/options.go create mode 100644 pkg/util/excel/readme.md create mode 100644 pkg/util/excel/utils.go diff --git a/cmd/internal/dao/artistinfo_statement.go b/cmd/internal/dao/artistinfo_statement.go index b639665..6b544cf 100644 --- a/cmd/internal/dao/artistinfo_statement.go +++ b/cmd/internal/dao/artistinfo_statement.go @@ -137,25 +137,25 @@ func GetStatementDetailList(in *artistinfoStatement.GetStatementDetailListReques res = []model.StatementDetail{} var orm = db.DB.Model(model.StatementDetail{}).Order("created_at desc") if in.Condition.BatchId != 0 { - orm = orm.Where(" batch_id = ?", in.Condition.BatchId) + orm = orm.Where("batch_id = ?", in.Condition.BatchId) } if in.Condition.TfNum != "" { - orm = orm.Where(" tf_num = ?", in.Condition.TfNum) + orm = orm.Where("tf_num = ?", in.Condition.TfNum) } if in.Condition.ArtworkName != "" { - orm = orm.Where(" artwork_name like = ?", "%"+in.Condition.ArtworkName+"%") + orm = orm.Where("artwork_name like = ?", "%"+in.Condition.ArtworkName+"%") } if in.Condition.Ruler != "" { - orm = orm.Where(" = ?", in.Condition.Ruler) + orm = orm.Where("ruler = ?", in.Condition.Ruler) } if in.Condition.SaleNo != "" { - orm = orm.Where(" = ?", in.Condition.SaleNo) + orm = orm.Where("sale_no = ?", in.Condition.SaleNo) } if in.Condition.CompleteDate != "" { - orm = orm.Where(" complete_date = ?", in.Condition.CompleteDate) + orm = orm.Where("complete_date = ?", in.Condition.CompleteDate) } if in.Condition.Id != 0 { - orm = orm.Where(" id = ?", in.Condition.Id) + orm = orm.Where("id = ?", in.Condition.Id) } err = orm.Count(&total).Scopes(db.Pagination(in.Page, in.PageSize)).Find(&res).Error return diff --git a/pb/artistinfoStatement.proto b/pb/artistinfoStatement.proto index c5af79e..bfb321b 100644 --- a/pb/artistinfoStatement.proto +++ b/pb/artistinfoStatement.proto @@ -25,13 +25,13 @@ message StatementPageInfo{ } message StatementBatchRequest{ - int32 StType=1; // 字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go - string ArtistUid=2; - string ArtistRealName=3; - int32 FlowStatus=4; - string BatchTime=5; - float MinPrice=6; - float GuaranteePrice=7; + int32 stType=1; // 字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go + string artistUid=2; + string artistRealName=3; + int32 flowStatus=4; + string batchTime=5; + float minPrice=6; + float guaranteePrice=7; int64 id = 8; string createdAt =9; string updatedAt =10; @@ -47,16 +47,16 @@ message BatchCreateStatementBatchRequest{ message StatementDetailRequest{ - int64 BatchId = 1; //字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go - string TfNum = 2; - string ArtworkName =3; - string Ruler = 4; - string SaleNo = 5; - string CompleteDate = 6; + int64 batchId = 1; //字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go + string tfNum = 2; + string artworkName =3; + string ruler = 4; + string saleNo = 5; + string completeDate = 6; int64 id = 8; - string created_at=9; - string updated_at=10; - int64 deleted_at=11; + string createdAt=9; + string updatedAt=10; + int64 deletedAt=11; } message CreateStatementDetailResponse{ int64 id=1; diff --git a/pb/artistinfoStatement/artistinfoStatement.pb.go b/pb/artistinfoStatement/artistinfoStatement.pb.go index 533f6e8..b18d5d0 100644 --- a/pb/artistinfoStatement/artistinfoStatement.pb.go +++ b/pb/artistinfoStatement/artistinfoStatement.pb.go @@ -94,13 +94,13 @@ type StatementBatchRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - StType int32 `protobuf:"varint,1,opt,name=StType,proto3" json:"StType,omitempty"` // 字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go - ArtistUid string `protobuf:"bytes,2,opt,name=ArtistUid,proto3" json:"ArtistUid,omitempty"` - ArtistRealName string `protobuf:"bytes,3,opt,name=ArtistRealName,proto3" json:"ArtistRealName,omitempty"` - FlowStatus int32 `protobuf:"varint,4,opt,name=FlowStatus,proto3" json:"FlowStatus,omitempty"` - BatchTime string `protobuf:"bytes,5,opt,name=BatchTime,proto3" json:"BatchTime,omitempty"` - MinPrice float32 `protobuf:"fixed32,6,opt,name=MinPrice,proto3" json:"MinPrice,omitempty"` - GuaranteePrice float32 `protobuf:"fixed32,7,opt,name=GuaranteePrice,proto3" json:"GuaranteePrice,omitempty"` + StType int32 `protobuf:"varint,1,opt,name=stType,proto3" json:"stType,omitempty"` // 字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go + ArtistUid string `protobuf:"bytes,2,opt,name=artistUid,proto3" json:"artistUid,omitempty"` + ArtistRealName string `protobuf:"bytes,3,opt,name=artistRealName,proto3" json:"artistRealName,omitempty"` + FlowStatus int32 `protobuf:"varint,4,opt,name=flowStatus,proto3" json:"flowStatus,omitempty"` + BatchTime string `protobuf:"bytes,5,opt,name=batchTime,proto3" json:"batchTime,omitempty"` + MinPrice float32 `protobuf:"fixed32,6,opt,name=minPrice,proto3" json:"minPrice,omitempty"` + GuaranteePrice float32 `protobuf:"fixed32,7,opt,name=guaranteePrice,proto3" json:"guaranteePrice,omitempty"` Id int64 `protobuf:"varint,8,opt,name=id,proto3" json:"id,omitempty"` CreatedAt string `protobuf:"bytes,9,opt,name=createdAt,proto3" json:"createdAt,omitempty"` UpdatedAt string `protobuf:"bytes,10,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` @@ -323,16 +323,16 @@ type StatementDetailRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BatchId int64 `protobuf:"varint,1,opt,name=BatchId,proto3" json:"BatchId,omitempty"` //字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go - TfNum string `protobuf:"bytes,2,opt,name=TfNum,proto3" json:"TfNum,omitempty"` - ArtworkName string `protobuf:"bytes,3,opt,name=ArtworkName,proto3" json:"ArtworkName,omitempty"` - Ruler string `protobuf:"bytes,4,opt,name=Ruler,proto3" json:"Ruler,omitempty"` - SaleNo string `protobuf:"bytes,5,opt,name=SaleNo,proto3" json:"SaleNo,omitempty"` - CompleteDate string `protobuf:"bytes,6,opt,name=CompleteDate,proto3" json:"CompleteDate,omitempty"` + BatchId int64 `protobuf:"varint,1,opt,name=batchId,proto3" json:"batchId,omitempty"` //字段注释请查看对账单结构体模型 cmd/model/artworkStatement.go + TfNum string `protobuf:"bytes,2,opt,name=tfNum,proto3" json:"tfNum,omitempty"` + ArtworkName string `protobuf:"bytes,3,opt,name=artworkName,proto3" json:"artworkName,omitempty"` + Ruler string `protobuf:"bytes,4,opt,name=ruler,proto3" json:"ruler,omitempty"` + SaleNo string `protobuf:"bytes,5,opt,name=saleNo,proto3" json:"saleNo,omitempty"` + CompleteDate string `protobuf:"bytes,6,opt,name=completeDate,proto3" json:"completeDate,omitempty"` Id int64 `protobuf:"varint,8,opt,name=id,proto3" json:"id,omitempty"` - CreatedAt string `protobuf:"bytes,9,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - UpdatedAt string `protobuf:"bytes,10,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` - DeletedAt int64 `protobuf:"varint,11,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` + CreatedAt string `protobuf:"bytes,9,opt,name=createdAt,proto3" json:"createdAt,omitempty"` + UpdatedAt string `protobuf:"bytes,10,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` + DeletedAt int64 `protobuf:"varint,11,opt,name=deletedAt,proto3" json:"deletedAt,omitempty"` } func (x *StatementDetailRequest) Reset() { @@ -847,19 +847,19 @@ var file_pb_artistinfoStatement_proto_rawDesc = []byte{ 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xfb, 0x02, 0x0a, 0x15, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x53, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x53, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, - 0x55, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x52, 0x65, 0x61, - 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x41, 0x72, 0x74, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x46, + 0x06, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, + 0x55, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x52, 0x65, 0x61, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x74, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0a, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x42, + 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x69, 0x6e, - 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x4d, 0x69, 0x6e, - 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, - 0x65, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0e, 0x47, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6e, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x6d, 0x69, 0x6e, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, + 0x65, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0e, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, @@ -877,129 +877,129 @@ var file_pb_artistinfoStatement_proto_rawDesc = []byte{ 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xa9, 0x02, 0x0a, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xa6, 0x02, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x42, 0x61, 0x74, 0x63, 0x68, 0x49, - 0x64, 0x12, 0x14, 0x0a, 0x05, 0x54, 0x66, 0x4e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x54, 0x66, 0x4e, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x72, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x41, 0x72, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x75, 0x6c, - 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x75, 0x6c, 0x65, 0x72, 0x12, - 0x16, 0x0a, 0x06, 0x53, 0x61, 0x6c, 0x65, 0x4e, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x53, 0x61, 0x6c, 0x65, 0x4e, 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x49, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x66, 0x4e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x66, 0x4e, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, + 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x72, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x61, 0x6c, 0x65, 0x4e, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x61, 0x6c, 0x65, 0x4e, 0x6f, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x2f, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5b, 0x0a, 0x21, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, - 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xc7, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x72, 0x74, - 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x09, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x22, 0x89, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x61, 0x67, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, - 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, - 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x91, 0x01, 0x0a, - 0x1d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, + 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x2f, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x70, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, - 0x22, 0x8b, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x04, 0x70, - 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x72, 0x74, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x50, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x38, - 0x0a, 0x22, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x6e, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x85, 0x06, 0x0a, 0x09, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x65, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x21, - 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, - 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2c, 0x2e, 0x61, 0x72, 0x74, - 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x61, 0x72, - 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, - 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x78, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x6e, 0x75, 0x73, - 0x12, 0x28, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x61, 0x72, 0x74, - 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x6e, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x15, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x22, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, - 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x72, 0x74, 0x69, - 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x1a, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5b, 0x0a, 0x21, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x12, 0x2d, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x71, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, + 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x72, 0x74, 0x69, + 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x22, 0xc7, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x67, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x67, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x89, 0x01, + 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x35, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x67, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x1d, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x09, 0x63, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x8b, 0x01, + 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x67, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x38, 0x0a, 0x22, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x6e, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x85, 0x06, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x65, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x21, 0x2e, 0x61, 0x72, + 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x19, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2c, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, + 0x6e, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x78, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x6e, 0x75, 0x73, 0x12, 0x28, 0x2e, + 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x18, 0x5a, 0x16, 0x2e, 0x2f, 0x3b, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x6e, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x15, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x12, 0x22, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x66, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x1a, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x12, 0x2d, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x16, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x18, 0x5a, + 0x16, 0x2e, 0x2f, 0x3b, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/pkg/db/dto.go b/pkg/db/dto.go index 12e9513..1f75e1f 100644 --- a/pkg/db/dto.go +++ b/pkg/db/dto.go @@ -9,6 +9,10 @@ package model import "gorm.io/gorm" func Pagination[T int | int32 | int64](page T, pageSize T) func(db *gorm.DB) *gorm.DB { + if page == 0 || pageSize == 0 { + page = 1 + pageSize = 15 + } return func(db *gorm.DB) *gorm.DB { return db.Limit(int(pageSize)).Offset(int((page - 1) * pageSize)) } diff --git a/pkg/util/excel/example/demo.xlsx b/pkg/util/excel/example/demo.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bfaaf7e4a8b04e7dca97b18270dba65b5b5a1b2e GIT binary patch literal 9912 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 V2@Zlm000&8p@qQM?&pT?5im|8V{8)z9_a zyZ$|E&06PO=j@qxpJ(mo*?TL?!6V?pAVWWY8Xfe3haHQflQqD_!b#1-#l;$6>HO!K zjoH*1u%EtcGvSB}7T%gpvOhFM4)U)U6vq{Rql?EP<&e~%yKfB3VpH@6VNPAua zO*O~6r96C@n90hP%rCccV3Of{nh{ejk^BkfLh$8qAON%{N{n4NmP7W3_VQSLJ??6~ zjLdDRjKjUXJ2;j-y_4^2=k4gC3Z4q!N7G|Gd5^}!xw#?4K^(j3khknzD;@-EFo12t zk)S@TR4(PDPV*_h0G2&&)TH~wyuUulC3%1m7@uD;cX@%IjnoP}yG?BFVrkq~hqD1!)+Z5q_ zKUTQ&>(5Fe{_hibe>mljS#5@fVb|}>4Unb6%PtyBH!O6imF8?$LR<}9=e2LUP5>Wv zdT#XGz`LGf40rbPv(~f2iY%LBR`@wLyP?rqKm)MPsp{>Iu?Z{M6%F@rI4yLK4;{nZ zTWaN2Gy+Kjme%)}E*p6$X@D1(!q=DYiU!|#T=#ofWl*3`klmf6GJ zj``t9*_e`T9V-_2`1*vgYk^Fvu_!73un6~jZjJ!Ug~Q}T>gB|g?*-!H$s+Hg6&i|E z2n3$tZKC^bBKABXBYh1iZYY-<7aHCtZkBtpRqcBwqXFw`wyJ8X(t*|vJi>%ylmtC7 zLuY&`)I@h*dBew+Z0J0@=Vgz&#Il8rb-8ofY)aI$MEG3YbBVK%fT-HcaXcBH;3xGZ z=V}P9ml{q<=P{K;0H4>C@OH7MPa4_65vjriR(2{Z>XIb~}Blj+imL?PTAkYZ>jMxqK3TPL)T8BpM-y??eGh!}Q7WNh_ ze|@t(#A`q83xK2*4}5|60|VjWd(5UBaP>Hp0|?|g2Z`)wP!1Ik*@Ur$<4O-UU?FTb zWWXVg=V!nzx~kwJk5;to%u`!Ugwt~rV8)5lC?3FAKk?3^*mB3X7YG+1wN`9veL>Vh ztE$pzwjkuhMk#v9r0LQ@yvb+=tlW*GHYZ-VDDwvcliZV zM-!!q%9>`v6yV5=5q)_aJBHSzOx;(p6=$$~R&t-)QbGv_MnKzZVbbt6kQ0D#wU_W1x zCO|*BMr0-7CjWvZl$vD8bu@NkA+{$$v6ZgdK_@W_GZJbHPf2w?Es#Efs;ZhXR7j4VrbOh3&bumJup#(HBBFO6u_(4FdqN$R<~=be*9zt< zF;I?g%&h=ihk*1rqhkn;xSH2L4k>7M0dv4L5g{ACwwb!@NuLz!w6ZRq*ns?ZG#3*b z&+2$g_O0jq527!4tIH62( z0!8nGcGvO)h$7##5>sd~O*f0O=b7%W1-GSg&wLUf&{>=ZRJO-;Rl|V}+`ARp-|J6< zR4lJSw%^)pHU@FAG!-GtU4&%}U5ydjm+m-YZe+3&Bl-FBckfWa{ip?`up?%qNcEJ3 zs>alMn;#pDEH66Qed*tQvGz{cISXnKB&01mjws`}VKOwHH;C71f zRF`$P+?WyN831Fx+4Y;-uG8VbBqh%Z*4Q6<`aO(sxq`87s>SdE=R?meCW4smH1n56 zIer&6ffe{bHhmXzz>?HRp?4-yYv~kQHdE=;(ls)hr;Mr~wc0!Oa>kGAnsU-p?bOrB zxw{5hsd}-Sz`o05lJnr6$B8NP=u+h!mjV-m9%d(~iN z)dp=4;Ny=A!{($t@P2`MQe1I%aMoax*T(ocM!S;;NAz)NKgq?-61mOU&0P~7+M`8H zmPn*>lO&h0ZzkAo-ruGP4YV9bFA~X#UG-G>WpqW>nK#_M%N18VZ*wOgaY1)`8f63J zhW-`ZdOLi#O8+p0f^fj|9Yh$I04RO_J5yjm_nSMIDLXkhIt_vebC zYcowYSItE==sAWJvo4ypx|FrN*Hx=xZbZHT6cF^RW6XzlC^| zutgB$uCZA+JLocy>$nKT>I$8^x7vE{-aREFCp>>QxbFOfGfcfM^W90TYOf-ef;FpH z2^*U9n4@*PWO8iQ7v$a12CXP&cJs!5T+0tMS&RM*USd>LDHsQWCd`BD3ttHfx0heu z6yb$;>NQDRlTsYZHU|yHlQtR9etxyT3SY=9vDeleHA30RwK7s$jBJ-_a+21P)_A`* znQNG^N!Q~^a(Ho;LbhxXUF!mYM&ze5YW(b-xb=YM|5Qep&>heU=XVuZmL%s;%7Y8O zIAM4W!C^%Z#Zs=fmQ=ImDB<(9o8j3+D7);Od7nqT2=PN~_FTwN_&To;_tyS)`1t-t z%xyw0aH&(fLL+YAD;ODf0#C~|&M5mnllt=gCDpujt${5_0)#ub+4QDDL2#r7iMn2G zF=)4F#)|4=t(v{fNCT3U1qlpYD8Z%QzRV!r5{{}fx|jo3W+&)%7Q)6k7JLw2MY6qz zEhfi6MW>8Sg8{#ZZo}7OPJ#vB808phIWn6fbc4tJZRmzW6 z1+o{EFEuete3zOafY7x`^($eYrV{H*rF$YRU9ncYW^8q^I8oDK>5+iDoDh{Yz(RX9| zk6*qpdQPZE8*&-_{V2SO25(Iv!=6ESu4c1jsvcu7j^9LXn;Pik=n6f}+Jkj8ZZSk9tu$mWIXE=6;#i6U^-95SP*UQy=1%AC3Q2hPGYZseB ztuqv>_s|^u@A(=1CtjUhJnbx;ACmLeG$n_5F5C{tHEzgZoVbpYotWwv2d8o_JRCK= zmj(44ZI>{dgr|J;lG`lZto4Oys&9JUH6i@R_Bb4^6n^8!#?fAs{yvc7W5KaVLu<)S zjG-XpXZ%N9&M=!F`;nJ>hLzk)2Npb&Z?N!o6V~Qm^r6Chp=U9*ELCrFS)^Pv4U2l5 zU{*n7@OU7ekvU|cNrIVe5S2YjW%}Y%+7Q;NqJ(z753+dKGQG))7ap zfujCubp!%*^_Z~t7I_HXNReS!?2&%7J|~%3I}yh__%f4lFM09b7!2qFje~DaaA~yT zaGU9pios#Y_A|)plB1H8Q?IrOx0DlvR`$`?_`fo%D)z-Y0ZUZZkn*K?TL?Cl76c>C z^`FbiCr}0v=m8sPUk7t74&~hjjv;YcZDdoi6*iPNhP`S_fJfy+HYEemU0@G@#gIwr zPSFO!U*_!h)>zb^JNk5(P$%bO*}5j6YI^td^7W3)bfL1@eR{gn>7^q(PMa?FwoTw! zQPloXxSQqHl}Q~)fDe!Vxx*;ZvhW-n!E#9B!7Y8QUEi92sqVDny=)Iy6UWB2xHS^MA>{s!j8M{jK4H`1h4ETQZ znh81zcZf&vcl5>dy6#0n>c%Zee5bNMp4ECMGQj&30c07CK`gOLlAQSDwFrb9gc(Xo zT^`yxqb7~%u9WyZ*S%2LiZHjEnoFNM$xXu4*zsb@|E`X)RuqkMgmJ~-*?Ns#cNpb) z%6=p*aacFG(juH7LDwy3@SQf9*mhU;TTV2o#4=sMuN{TH9Vq&d?meIC-Rb?Qne24@ z9%aOZe9@h$3r3F#p{mLaj*X4Lz;~;fovk{&6*0_4Z{0UOsWmylXy74f*ft*J&p-k_ z!^nOcEVwXZKiYkE4lCeK)v=^Vn2!5))yvz&#cO}HWf2B^7`DPXJ3K5cm2K3m-Qv@@ zDw^*dWkweYTg5^P(+M+^3YYr@JZOiGsFMJ%*A`Iz>Qy(^%x6ryDI{IE7rwb0xK>0k zFT`>;(5yUB>N4S95Og3kT?K6pxrDy2G?97CoAq$zBnXC0l zf1Ht%l|pL`qc6*rbdgg*rErD$>x^zUy7A(@QdMFcZsab&{ue^DDvZVPuk6{ntIdiu zf#RfW`mT3Bt}Bi1FDC?E)==wGNp72+ea>B!jTfH^@WP_fm*$VeOE=UE_7%jBCGLKm zac;>D0V8VGi%fLv8V-bgG-Z*%E=MW4Zlg@1Rf$XL0#+gy{bSuYwNBxZ7-}mbKg+u#k z{{>B6)X~(3g8;u)>3n&2CQE)|5p0GN{957itpK!26`4u&Srk{jP`hbAhjZA>deEV+ z_`b^lnSI23$y5eaWR)i5u|;KW61AHU2-Kpl-)?Y~*zC@kgP=yLh`%~{MvbqRM%}MaqC0WKarstM>r}E(9d!mN{NDrWd;xvXx=1ilzd8+f#P8XR^ zP9Cdu#kMe)SFTW!#{Rqo_ROUEvKUP=te@tB$h7_jwO+`Bxy=>r`14{jeH6=9%QFp> zETFq&ZFh0pSR6UcR6V=#7%7~13hxuE3_TbOLt3YVK(3B`(+R4{&?_aLz>$;f)N|@U zG8?BoRljA#d;AiXD`9KWqZ4HDVbSWT9$}XN+2Y~SW$EjuYlcfo8T|FDNg}Nt2kV3F zwlDREY#sS(-A4E-)Cx0 zc~3HKi6OF{Co)xqkuTytIx}`}kIP#ydmVq1`f1WM=JK5 z#)OEOu}B5$pKOzEl3&jqSh)K&a2#Ca`3oDSpS#^&xqScd!smKAx^kiwa-DIvYnClT z$tCTL6-8M~ zU6c~JXSn7=xx@|CeEcbDSrd%=3W6ii3MsvH#{2vq4W2okw2=OdmTCL%h(5|w>JhII zXpBRW2ltsyD`pFsl0y1p*ULGT^QaK&DsqGvLe09oJie8~li`IFPM4#|H=#Z?bwT?^ zj*;uxUj{$1=Vl4oWHXPo;LEs58;$2f43%rlCkH4a@cWd7w)nfF-w9{BcE?F8>U2}2fjdT}^%!fv z9f2VuwKM{_!W(pQ-da>%Zd0*uNNphV8o0bV1^57f9*5tF+uE$sdy2 zQKt{ukD;lykMHKyK^_?H8BVHUw-A%RxDx|v0?TTXbL2`{1pPDbd|k*;c!5I0_j2#d zpFDY{F;l_BcuTtn?w75s+RvzJeS)77t@%j9>5{HZpq3{taaQ?Exch_!*YhJsy7B7} zyl}92y<%>E!Le_GyN*AjK(ox3Pcv;M!Q*BFofEKuq1`R@Ze(}<47@$c>lG6SZSjUr z-^40(o75s~-tA&@4`Fc+iVB*`?@?Sg^J`Z2$d>iw9kF7^7rex8;+%qeS^FYdR97e?~k`c6uUdsQ8AvaP+I@QFU# zOM*{@`d+KdTY^iauz-@%n>a%bJafuEI>ABRiqgz;C`4yT#oKC~OQ)2fw-E0wRr85L zDzVqz)^?QXO17+~FB~2#WgjGg(N|I4yx?XMT`@=L(2L=ZSEtrjjl?uUk$sOA^F!gl z#$mqV2dtefsL_jXl3uoubud6<>W2x|%Zde|b@baji#pWhN1!AWRyovI2!Io#oJL91 z)41F!Vd?8~|EeFk>n7#fzQ@g%EPq4xxM_OQ9tzj)zY;Gj94^eS5$^9}w1*My|F-|n zfv&RL|91NOr0HP-_sdeCe*^0DU*>Ut`uV*N`M}-3tRLFRg8KP$O7*9M-y2^K?aE*F z9V#9ENtyrCy!`X@?`@%n=GZUmf-Zk+mHm0}_xk*yYxK+3(EgERe@`O+Jp8-1dnn<5 z84&90fg1lQ=>I(Wy9#;`f4?jd@1NiOFDdw^!`~_Ue_uc${{JbZ{&es=e?2h!FO!C* z$cLBtljr~R@O!>};JIJsL-gAf{>^%S9{(Nt4@vo#VL-Jr^bh=>DOy<$5$Rzk8uUX8 Lg<9v+ht>ZBn-{jF literal 0 HcmV?d00001 diff --git a/pkg/util/excel/example/exportExample_test.go b/pkg/util/excel/example/exportExample_test.go new file mode 100644 index 0000000..f852823 --- /dev/null +++ b/pkg/util/excel/example/exportExample_test.go @@ -0,0 +1,70 @@ +// Package excel ----------------------------- +// @file : templateInter_test.go +// @author : JJXu +// @contact : wavingbear@163.com +// @time : 2022/7/23 15:59 +// ------------------------------------------- +package example + +import ( + "fmt" + "gingogo2/utils/excel" + "path/filepath" + "strings" + "testing" + "time" +) + +//定义sheet表结构 +type sheet1Define struct { + Xid int `json:"xid" form:"xid" db:"column:xid;comment: "` + Name string `json:"name" form:"name" db:"column:name;comment: "` + Age int `json:"age" form:"age" db:"column:age;comment: "` +} + +func TestExcelTpl_WriteToExcel(t *testing.T) { + var sheet1Data = []sheet1Define{ + {1, "张三", 16}, + {2, "黑猫警长", 18}, + } + + var sheet1 = excel.NewSheet("Sheet1", sheet1Data) + //var suffixFunc = func() string { return fmt.Sprintf("%v", time.Now().Unix()) } + exCreator, err := excel.NewExcelTemplate("demo.xlsx", "./", "./demo.xlsx", sheet1) + if err != nil { + t.Log(err) + } + exCreator.UseOption(func(excel *excel.Excel) { + ext := filepath.Ext(excel.SaveName) + name := strings.Split(excel.SaveName, ext)[0] + excel.SaveName = fmt.Sprintf("%s_%v%s", name, time.Now().Unix(), ext) + }) + path, name, err := exCreator.WriteToFile() + if err != nil { + t.Log(err) + } else { + t.Log(path, name) + } +} + +func TestReadExcel(t *testing.T) { + var file = "demo_1671435052.xlsx" + ex := excel.Excel{OriginFilePath: file} + var datas []sheet1Define + err := ex.ReadSheetData("Sheet1", func(rowIndex int, rows []string) { + if rowIndex == 0 { + //跳过首行 + return + } + datas = append(datas, sheet1Define{ + Xid: excel.Int[int](rows[0]), + Name: rows[1], + Age: excel.Int[int](rows[2]), + }) + }) + if err != nil { + t.Error(err.Error()) + } + fmt.Println(datas) + +} diff --git a/pkg/util/excel/excelInter.go b/pkg/util/excel/excelInter.go new file mode 100644 index 0000000..1070e6a --- /dev/null +++ b/pkg/util/excel/excelInter.go @@ -0,0 +1,253 @@ +// Package excel ----------------------------- +// @file : templateInter.go +// @author : JJXu +// @contact : wavingbear@163.com +// @time : 2022/7/23 15:34 +// ------------------------------------------- +package excel + +import ( + "errors" + "fmt" + "github.com/xuri/excelize/v2" + "io" + "log" + "path/filepath" + "reflect" + "sync" +) + +var ( + ErrSheetNotExist = errors.New("sheet does not exist") + ErrSheetDataFrormat = errors.New("sheet data format must be slice or struct") + //ErrSheetNameNotInTemplate = errors.New("sheet name not in emailTemplate") +) + +//====================================================================================================================== +// Sheet define + +// 抽象工作簿 +type Sheet interface { + GetData() any + SheetName() string +} + +// 实例化工作簿 +func NewSheet(sheetName string, datas any) Sheet { + return &newSheetDefine{ + Datas: datas, + Name: sheetName, + } +} + +// 定义一个能够通用的工作簿结构,此结构必须遵循Sheet接口规范 +type newSheetDefine struct { + Datas any + Name string +} + +func (s *newSheetDefine) GetData() any { + return s.Datas +} +func (s *newSheetDefine) SheetName() string { + return s.Name +} + +//====================================================================================================================== +// Sheet define + +// WriteToExcel 通过模板文件写入数据并另存为 +// param fileName : 文件名 +// param filesSuffix : 文件后缀名生成函数 +// param fileRoot : 导出目录 +// param templatePath : 模板文件路径 +// param sheets : Sheet类型的数据,类型为[]Sheet +// return path : 文件路径 +// return exfileName : 导出后的文件名 +// return err +func WriteToExcel(fileName string, fileRoot string, templatePath string, sheets ...Sheet) (path string, exFileName string, err error) { + var exc *Excel + exc, err = NewExcelTemplate(fileName, fileRoot, templatePath, sheets...) + if err != nil { + return + } + return exc.WriteToFile() +} + +// ReadDataFromExcel 从excel文件读取数据 +func ReadDataFromExcel(filepath string, sheetName string, handler func(rowIndex int, rows []string)) error { + ex := Excel{OriginFilePath: filepath} + return ex.ReadSheetData(sheetName, handler) +} + +// ReadDataFromBytes 从io口读取数据,用户http上传的附件 +func ReadDataFromBytes(file io.Reader, sheetName string, handler func(rowIndex int, rows []string)) error { + exce, err := excelize.OpenReader(file) + if err != nil { + return err + } + var ex = Excel{ex: exce} + return ex.ReadSheetData(sheetName, handler) +} + +// 新建工作表实例 +func NewExcelTemplate(fileName string, fileRoot string, templatePath string, sheets ...Sheet) (exc *Excel, err error) { + exc = &Excel{ + SaveRoot: fileRoot, + SaveName: fileName, + OriginFilePath: templatePath, + rwLock: sync.RWMutex{}, + } + if sheets != nil { + err = exc.AddSheets(sheets...) + if err != nil { + return + } + } + return exc, nil +} + +type Excel struct { + ex *excelize.File + SaveRoot string + SaveName string + OriginFilePath string + Sheets map[string]Sheet + rwLock sync.RWMutex + Opts []Option +} + +// UseOption 使用可选项 +func (s *Excel) UseOption(opts ...Option) { + if opts != nil { + s.Opts = append(s.Opts, opts...) + } +} + +// 添加工作簿 +// 注意如果添加相同的工作簿,之前的会被覆盖 +func (s *Excel) AddSheets(sheets ...Sheet) (err error) { + if s.Sheets == nil { + s.Sheets = make(map[string]Sheet, 0) + } + for _, sheet := range sheets { + var sheetName = sheet.SheetName() + s.Sheets[sheetName] = sheet + } + return +} + +// 删除工作簿 +func (s *Excel) DeleteSheets(sheetName string) error { + if s.Sheets == nil { + return nil + } else if s.Sheets[sheetName] != nil { + delete(s.Sheets, sheetName) + } else { + return ErrSheetNotExist + } + return nil +} + +// 读取工作簿 +func (s *Excel) ReadSheetData(sheetName string, handler func(rowIndex int, rows []string)) (err error) { + if s.ex == nil { + s.ex, err = excelize.OpenFile(s.OriginFilePath) + if err != nil { + return + } + } + datas, err := s.ex.GetRows(sheetName) + for i, rows := range datas { + handler(i, rows) + } + return nil +} + +// 写入到文件 +func (s *Excel) WriteToFile() (path string, fileName string, err error) { + if s.ex == nil { + s.ex, err = excelize.OpenFile(s.OriginFilePath) + if err != nil { + return + } + } + if s.Opts != nil { + for _, opt := range s.Opts { + opt(s) + } + } + //插入数据 + var cellNameList []string + for sheetName, st := range s.Sheets { + var firstRow = s.getFirstEmptyRowIndex(s.ex, sheetName) + var SheetData = reflect.ValueOf(st.GetData()) + var SheetType = reflect.TypeOf(st.GetData()) + switch SheetData.Kind() { + case reflect.Slice: + cellNameList = s.getJsonFieldList(SheetType) + var rowLen = SheetData.Len() + for i := 0; i < rowLen; i++ { + var dataMap = s.dataToMap(SheetData.Index(i), SheetType) + for column, v := range cellNameList { + var axis = GetCellIndex(i+firstRow+1, column+1) + err = s.ex.SetCellValue(sheetName, axis, dataMap[v]) + if err != nil { + fmt.Println(err.Error()) + } + } + } + case reflect.Struct: + cellNameList = s.getJsonFieldList(SheetType) + var dataMap = s.dataToMap(SheetData, SheetType) + for column, v := range cellNameList { + var axis = GetCellIndex(firstRow+1, column+1) + err = s.ex.SetCellValue(sheetName, axis, dataMap[v]) + if err != nil { + fmt.Println(err.Error()) + } + } + default: + return "", "", ErrSheetDataFrormat + } + + } + //保存 + path = filepath.ToSlash(filepath.Join(s.SaveRoot, s.SaveName)) + s.rwLock.Lock() + if err = s.ex.SaveAs(path); err != nil { + log.Println(fmt.Sprintf("save file error :%v", err)) + s.rwLock.Unlock() + return + } + s.rwLock.Unlock() + return +} + +// getJsonFieldList 获取json字段列表 +func (s *Excel) getJsonFieldList(sheetType reflect.Type) (tagList []string) { + t := sheetType.Elem() + for i := 0; i < t.NumField(); i++ { + tagList = append(tagList, t.Field(i).Tag.Get("json")) + } + return +} + +// dataToMap 数据转字典 +func (s *Excel) dataToMap(sheet reflect.Value, sheetType reflect.Type) (dataMap map[string]any) { + dataMap = make(map[string]any) + t := sheetType.Elem() + for i := 0; i < t.NumField(); i++ { + dataMap[t.Field(i).Tag.Get("json")] = sheet.Field(i).Interface() + } + return dataMap +} + +// getFirstEmptyRowIndex 获取首个空行的索引位置 +func (s *Excel) getFirstEmptyRowIndex(ex *excelize.File, sheetName string) (index int) { + rows, err := ex.GetRows(sheetName) + if err != nil { + return 1 + } + return len(rows) +} diff --git a/pkg/util/excel/excel_test.go b/pkg/util/excel/excel_test.go new file mode 100644 index 0000000..c333e35 --- /dev/null +++ b/pkg/util/excel/excel_test.go @@ -0,0 +1,38 @@ +// Package excel ----------------------------- +// @file : excel_test.go +// @author : JJXu +// @contact : wavingBear@163.com +// @time : 2022/12/19 16:52:11 +// ------------------------------------------- +package excel + +import ( + "reflect" + "testing" +) + +//定义sheet表结构 +type sheet1Define struct { + Xid int `json:"xid" form:"xid" db:"column:xid;comment: "` + Name string `json:"name" form:"name" db:"column:name;comment: "` + Age int `json:"age" form:"age" db:"column:age;comment: "` +} + +func TestSlice(t *testing.T) { + var sheet1Datas = []sheet1Define{ + {1, "张三", 16}, + {2, "黑猫警长", 18}, + } + + var sheet1 = NewSheet("Sheet1", &sheet1Datas) + d := sheet1.GetData() + v := reflect.ValueOf(d) + switch v.Kind() { + case reflect.Slice: + t.Log(v.Index(0)) + case reflect.Struct: + t.Log(v) + default: + t.Log("错误格式") + } +} diff --git a/pkg/util/excel/img.png b/pkg/util/excel/img.png new file mode 100644 index 0000000000000000000000000000000000000000..e20fe426c38e8a78ae4fd7276b66943297e2f6a2 GIT binary patch literal 6648 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 WlH9gce)>N;Oa`Y+bn~?@-1$F_QW9YR literal 0 HcmV?d00001 diff --git a/pkg/util/excel/img_1.png b/pkg/util/excel/img_1.png new file mode 100644 index 0000000000000000000000000000000000000000..fbf06bd6e4bcf55368ea09f730182f2d85ce157c GIT binary patch literal 26183 zcmcG#by$?&^FO?(h?0r|(n>cSzz|x=+vUJ0e(%s#^ z+xPqP`9Af#o`0U}*?;!F?)%I+bLPy=-6!Iy1rrl|xMYXiF9G5|e*&MpyEi&IKK1qWuBTNG z0nTO&))^)GQ4u zyvu-B_^UuG4@wMbA_Bg>?d4ic^to7B$}>-Gy4am-Jio7!;Gh=m$mMgWe8?P56}64# zkK|Z0C8Rn#Z#9&Bo?jgEERePZIf@o6+fG2&_GdUcyJccf$Rp z37crs&5RVLei{p{8xlq-$YtE@b#ry@xYBd}P0(T)kCfvb$_BHyS3dpK$QS<-r^RxM zvv=nqlL{x(p!V$Ni^M!ArQ;M&bu^fJ2g)~c&~_hy@z%lONl+a>pn zK{ee4hq*{541F_|F?f4%T{L?~3@=R_9L)w?RP%!CE=zzg-W<$*VLS z9N}1(y<$|3HjtfrlPa${b=WUac4^lg$9X#Eb8A~O<06X>OP65P=wG}$i9h*lW1_31 z=bOQKR-(1{)jIQ<@x_&WQH{9@U8RBE z`jwlatNjiMIgPkF%*C;|xWtLS+Pyvd?06AoZ=e|tEm+kl8l9}Q;5F|{0oDvXl0eRD z7TJCO{(URUY%U#ZYf7dCcEg%kk0N3!f5+odJ>^QK4E)TgWCBj4^*C45S^q0KjS46l zEA`KF%TL@+!iRIeJMDcOF!8n|lE0!^6=`W#~)3k$* zz}XKhqAgmNP?Nq;RU6J|{Sd2QtJd1G^fwqJnWZPI>;~z^CEhOU*O4412d`8D1p#WU z0@mTV>-AgI9(Rw)y}Htca-2#Oa|l0)zFiHfRNYguKPu|bo5 z29vP!v7^3-72Ra_6N&D95s<*1EC&puixUMxZ?K#u>h$-@F{tq+J6UC!x>ty~_p+s& z#A&jwkZR_zs~+ZVu-9<%n?$e^McN2=Wm}_&B4{M`0*$Yq!J5t12rpD_+0ZwW%e%+S z;KV``Yj^x(;kuGFmb4)Qajm|%;;#^+j&aj+8w?LWMT2QKX~tk&RU@Ixlx+>#6rUx+ zfFnS_=KNsUA%s&BiEBmUrT?+suRSl1g(s=%9Z`w#vWH|2T2x;z9HU%Nv$LAGj(EHRI!Bj2FNBRY3Y=( zf_IkK&<^)!*6Wj)t8~TRzd~XtoWw6N{e^Q*%j?d#HmL*n;!zM}l?l13ZhGT+nI1t| z?fYek776Q2{FvToXr0`MIJzMpS4QHy8iT8SsZ^=K^4se4I$}m$JDOb6Zz4=ddh(+| zG0pw#$-L6Gg|Oh^Twsk8Ao(AYt#c3c@;Y+fVCS2yvkyv)SHZPuZSZh;M3YS>f~s_v z{K6S7|1P&8YIdtXj_L0z3$XI~7bk6(oviS|P5!%Hx9D`yk?WCeWKZW5x}>$rj%0fjgL;Kbb#WcSqYpRpg-GVzx5p z;8R1zPuomV+NxeNwv&E*mb?wLaQxO?BGKn$<&hrTdY)n!>1ao^LwgY6jHF}j58aX! z?ZSGF0z{O?vQ zs50krB?Rx;D7UITD^cikk&%bPEWJ*H#?@h$1++tmbF>bgaoz z30L8%-;)z(RTp(~T&73H6Ms3>#?<+L9lreW2a18VGS{p(-x_UJBcf+n6dze3KPQnj{*NUq)_LnQA)GQn#LrR^bGP7WVSv0W&6ODV?VEGWo)$h@zrs zia{g_MGPzb6*DL~$ylqsUSz78m&_WRshkL5QRscoM7R`gEq7RK%+2NK|D;1nldR{< zVs9eGMpoRy7&#IKbGg;M;jo)8{0_xQnwij#{<$9EW*6EsNbF#>)WiBb11j*Vhuv<+DGAos)_kEE&UkTEbU(hcpo~s z>KpP&$vXWP+pbo>=baA_7LWlFGxh_iOL#BfJ_IOBX~dYhMbZsr#>P9&W1+_`t!bkR z9@MTZfLyqnd1%1zr9GXcgGtZK9|+F6Whvyvs80kEPTsV~!?#*WX1R7FQd5@lTHRu| zWQHs8(;C)vI)b4`^-p#NT(=%(m9gokd;ie=PQr8XRho2~hg=LsM;`Ug+*A403aP`T zo)b+xoWnj(|HbNR$_4JR?Mf5(M=8B*6dkaA*&xzJZ-(S=_VoI$Q*WKnw~S1{2C_D$ zEL%qMGi7PJ!XUMEwDe5au=v6pwyPt59QU@5Q8})Ba{8t?&a(`&h$`0S?JudB zO_KN)6s$=4MLK3U7*|rqELH)-#1&|eW*DY_%gP`&F-zg)RW<~rQS6SHrnqE!vs zg7hkUV%_SCxkTz-rdj8;c1laW*a#}%Q_RfWZ>qq^F!as@?*T&nmS{K|wa(pgb?QL! zJU}?Y3GTgFQsc5oZLA!}kwL(6p>~h@#AZ;jH0Qzc7R_^;E=$>MWzt&H2lhpr(n1Y5 z50V}9A2UEW0|Eo%IU%U`Kfek&u5?O)T?e{W`_o#y&JV8o!%8UrOB>0N|7fH=PLZ?= zBPKZk2<|yJGXCnmP4boT{z}+*;mqPzW1WKlNN8Zr-3tgGyD;Hjpi)L^sltrW^}m5Q znHAj(`{^V^`Gu;{87;5P?yS%$d4x@+-!= z-f|Y=b3K@LyHlpFc*1=fXaoJ*smVdw$ZB>N3Br5-d?g5x)Rva;k|e?CcE=S;55&HB z9kHUVO6K2@UwvM2K4ov39(1=1sdGO?qq zn%jkbOOMSm3AW!eFHMjtKq$Xz=C~3LF^zU0V88=`Xndhqpda*+93YU}z5n;4!~f^T zwJ}?nvLQh52eo1my}dfj%F5#5SBqB!fd<-gptL?$`}!p=zo}h2$rV|q{K1460B8$6IzUSo2Rr9O9-PWXgxO4uDmZU&gcdp#=L;mrA0g{s> z^?X~q?}MM1)Kt1XM47a`b3~;rDj=g3(oJnc=Ni`CJ}p%($={wu4yS5QWg0J@s#llbq0B#9#^ws zQc83)lH-#?HcmA{1_?Ax(FbW4qz-~8QrFpnurUt0wMT}?i-B*Gv^?o2@;QcI%`ICg z#C!;>Fh|=w`~eY)JkA#1i70``gPLtqHkQd}b#erSKa)18Rivb|YmaCw`ab%7y}R09 zC2%oZ0xd|zZ)3hUou4lm^Sb~VgSaP+d}ArH8#yz$9aTI}nx9jP>xLK2FX0~{@I!Vo z7dlGLUQI%5>R?UzR=d!&Xi=d1`(^}Vj@c;3@e&cLC1JqmnbD ziquAlHP(WG#|V*0hX5Z1M*nc(5djM=-lG?dDJD=k*;IW$CbYfd{dJwTc2D26o|S0@ z*$iZF5GLPZmvm0#5$&<8QZMq5&^?kPt;X~tMKlVWDw)T#GxLa zaCjaadM5!tXEYH~`?-o4JnM#Q&S%C#LzNvPEf!;QrXa`Z7oj7{7Zqk88(O#OM%N%s z4pY{|6ClxIxiTysjBEHUdFp|z0wNtQ?E$7B_9x`=xUb(ut!@0UEaIP(o(&llbkNG) zUdl?{c)$P9^Zo&Q0~QH?MDSY!FSo^4RLl7px+VFKJf?QEkx%y5TUfQMbaK7wSc?=o z-#SI=>AX{>$>Q(1g*p!x@%=EZQ%=viQ@AG;>k$t{CXy4+Ii6A^`3XLQ)n9e zC^`F$qd#;sjjboHl4&7xK>bIc{x;1DMVD`&wWfN_`8@&WIK^&|NFXY(tCF-qXWJts z^Co*^ID#}}s};L?g=Xnvx2-%jbyMPK66HroByYovwsR*#tVrZm%Gm}=flr;JPNx7- z>~8nYu_P}Je<_aRl#VruXi_v+H>6Cwf^bdU&atr1K*~A`rN&Mcx3oK|r8!TKJL87} z_c?|&kE`-QN{sS8Bp*xQ1S_~aZ%&rxNBosxGgoD^ZE9-($&3zl-^<9wod<*iI^(0? zT;!~1rWN{vGMl)Xu>knE?G$1`+1w zlTAtFlir_LrJz#$!(f4&0S-p+6AewoGZrpzaEL@i2bs(IU4C(zX+>FY(*};<2%YMc z*W7}4#NUe17!%TbcWud7`8mxYChqx%w1eJ)&JUsqX%T4ghw3EqUI2X=(+JbY7dW7Q~E*4Uk+6auH?n zF%5Oh8JJk34LCU7T8Z*j8Ba&LueP}&3W>GON7E1}7rNQ^x&&Glwa*`$Jn0jK@+GVN zncu=&40+VY&@C@r-7YY)lhGYsz^L4{J=iaEtlp~;k~An&+xfI#a2Ki$1aV>^N^u{&s0d3b=J{`nrl-WH`*p4+DfFu548PztwV=KWBMQRmFk5v<{BG7Jh> zB^~VP>EPzc51V)5*R#pC5TNFH);hN=ht$3(ezvnVXg*b&Jt5}QL;Zu@Z@(#cCEg(u zBcpvozHbYm`VPkMD?8a{od{NY-x&ETjoc*DnVJ zVo@&9K^viquJfW^v#i2o!e@=O9GGWK9jQO6)530Ys+V>|ndQ`JgMXV(E5&`L2Md3% zFv3Z?B#w70aqr+f(33$H&Xu@GVC%s#Uv=cGNj>8xspLy|V(7s;TN1NYDlTfG3w8*C zJ*AOeb;R3XJ$d6%QK#4Uj=|iAq2MQS?8FT?#(^3>gl&3FXO%3B)Q1oQspa=4kOEv4 zTh`ZJJGWyCW1Y-KR(LoIo@#JB=eGA=epn@OzprC@>)85(9``CL##P6%vBv&>5i&{5 zm0%J6`ZMKQ`j=J3!V390DFabZONjgPMpD`Zw=Cz8fwRz+-trV9Z1h1+fPQG5aZWCK zD`6XTspEnO)%o3BqPzP|%PI24<&O7p3CRYGOn5n3!-lrG3e)XZ5n0VyY@?bP>(s(x z_ejjT|8lQ6?|44Gz{>{HwbIzzSA>i(P0MB~%sD(~Fy_NFyE5vKJcv9)oeUXEO8 zP}Q6TO(1ygy9gWir3IE9+KixkvEruE(B&vs+e2}i?p{uEqp zqBL@yS$#jxn*B$JE$&`ykna=m>AT!Ne=4si&`;CpiBuVVW>rIU;B_ePw%< zn8Q-%+1c4oKb+I;sTwj6Nb+$pJZE-uFiYOp``on4X=9`SNT6=6O5D0r|NT0|s2m{X zb+&hNeObVKc`{|+?GNArOZ2ixn|B*O5JHY}W;g)T_gFD{M()dNgISj++kPfkpmfJ< zXauc<_u9aV3o`n0ASf4lh(3_~>3bLFB_VK9P)mSw%#}Ou{gJwO?ugwM#|eQF@8Ne* z9q~Ry(}=j(0Ll31=x822$@hWWv@t?!=D7@>2TN#r`S8%t&~IWCA=J%YZ&%XO6ctYP z=37NvcVfV}NRQ(UW`vtGLwH0)nQ_Yv!gB{qT4^;}sG2O0D(tKTW%u=-m>0D7)@1?{ zcW2OcQGqD6;9{Q_?iGCc9Zt=oETw|Bn{7;NQ(9*j+kO?}`>g>upH@`75;K1oQ`x#p zLk;_LL+gFp92-##J3FiR=zua02%yP~CKmI*$(9kzQR!lJ1QiCwaFD+>WBne5 zisXCO;9`4@;4}$juIOvInM@{~{WLXH#+h)6o#b7=ESrdCb`m}~E}P598%Rq{%c@d+ zXi6w)3QTM{-q?j5gWt-t1l}0x(oQ>@olo<&8m=BUxXz-zdC#-5cXs9)=aVc7aqu~T zD|6|lf-fX}m{#M1cX@EqyZ9KoPwk_iHMUbsl}}|9Q8^|-T6t<3=BS?3fm2u5VP9Fa z4yHVq8@AtpvvZr#vk{Grw^Go@0RRBehsy1)iFek*uByRc4>vvy~vJ#COoLab56+ZT_k<%(yc zbx4MGsnG$gwE|!S
mE?dvMZ691om4x&mT(>6nFE-0I*Nu|X$j}F>`s&X*i(jc9 zr9+9hZnB!juZDd6Tl$8_OPgZ0Slg^VqI(bZ3{G!p&KB(Vt4_O&pZ9lhf5LJ#YnTly zS!Fo{>?9C5<%C1hJ1zOPG;Bf9$&EY;44c**dF559W_@Eyrl$z^* z{n73%e}2=tUO>E(iWSFfdT-mP4kSTAWDi1(+smISMRPW&qIf zf2lO@@6rY}N?ngdMLoXRYB+b+LguNZ>Q`IEmIPY!P_+iw*3C^?cuG+lBF)w9>bA$Y zVe27+g`4`A;jiw-@0?Es`)I(s5TqzYH+`jpQC$_sVH`Q=Q@E9LbF%R`Lw5ip?saV( z!&H#TW;m{PJcqLytr2f_sr7QA0HK7U)d?HeSjkR$lfVgU> zlf)Oh+uOK`RC0D3K%YTeG;B+2=b_irNl_g#b=>*_&g)e`Y-_BJ^IR$czFq&9wW>(y zocHB+TA~U9;kBH{mpXeEVIP`1CpCS)#-2iAmA0nwki8%F>D;kFz42$z(fr*xj{ssz zQd0#kjr*M4qERp4X7G@ezF0-A4#s#UokCXq3$ib)NXBHBZ2QPp?6C(75bV}5PHg=c zX*grn&_j{iv%&#}mTD4);alX9f6Mito$hu@hFFRk?YlC*5FVEEIJs_~w5~^#aTMjH zeYtGwKCKC?+hS2qoy~0x-w({aV=N*(G;2U7nC3B{b=%6FDuLr88ZNwyF9K+;{Tt+qBd9;UZTZ{S z4~ybE9y`z;Lwo$^U&Kw9yJ9cW8eF!Ad_Ex9z_I~~ykGo2rmRpgfh2MpgeJ}Ws~wM` z$MUEl0yPDL0fwtgWl6rxfeh9ZC_?lFeYIrBoqt*t;cqP1Dlf<()~eO`J$pMWKir4? z2GCh3F~erW$Q@2x2FUj~Q#-RrKTsIo$31?SBIs2+#K&n_wtqE1njiD*p2-S3UZ* zC(psg*`yX4&oR<~B^BdeytGBWNwU}su;ZW4q{Y+hj4OWg9OLo@iZ_VtftZ1Q2t^ii zr#xl(hqZsq#}^8l&z5}g5B*~f?z&(b;zru1jSEopqGo)@6;n_*OxOjlYs4luMb3-=f zqW0IF)I<9b({QnCn}aGnw5FP4{SrK+;jEcls+-5>Zvu5^npF#H@9r@KSbGckMG2Uo zgfTqlnQrkrsSk{Sb`dY;;VNiUvY`E#<4HXfq843tHBtkO=Qc)(qk9r*%fihMW6rJ1 zno!o`r9hCZYw|?VSG<^;*P_ck`T3AOPr{%evQNEIkH7r3vzMFt-Pxvlu2ySJ90%Bn%ju%>MOf*PZEB1VNG#YBJwzirGqEM_w29c3 zMI)$^e6~C*9n|bY#Wk9t>y*8|lXhXF!oVi)@$?j)BfjNFB+jBm5M%DCfs}o*{MRlE z9uMns7&jL?qH#;gBHFK;Pp`q}h;1ccGkrX*92crM8&hCeZmw6NKS~7W7m4L$t|P@8 z4pBHM$PB16yCxC@{gv z7AEV~{o=^-Fm6ipTTLq&yjYw{3K6@h1iwc1UwzYC0WJM*`fHu}j*|#1+el4Cg4Wj7 z0v}SkM(XBB=wXCHl^4d``xm<-=wQ(Zd0hLu9@u}kDDfxXzYZXnI`N)5`5McZ2gTkp zsN?brjfbr;H}c>-moQ>b_An$_Y^?;`HO&Iyt`5@qvRnM{iBaO+NLp@q$>q`3$@Y|~ z=Hp*e^AM$zdHy#=y58!lz{btOfT>?LO*?;B)`Qg|!pKzlHOSov(2d&z znn=JcTuc6k!W*DKn2VHYBN@=zigDsa);j`dHnL%N*Ym&8E)YSLX9Dax7u#oz&Z8GSdI84KM@n^*J z(~Sf5r21Nj1#?F1FpeH^b_u_;~;bLpQrAMBNmLT#5mVQ5}T2oASq%*@1yf~TRc^#R<#az zj+$W3+|fXvI?T0uZ|z~7WYC&j6W~C}I1=*u{h@fRHIKgI8Drqf2#%kCUkk}pj!Vxx z5zI{yE*^VG60_yBIBR7FJIrWhnrYfNDY!zOP-;5b^CAh3#w;Y|mA!=MZK>PTo zU+84r8*$5jxPDhP(#9lzuAZH#1?pa$PIGF0Udj#ZSkg%22=2{9KjYNaah;wQBWn5S z&~yE`+=2Ts32bv{w^|g0tXZX1O@Q%oL@PPp4O6*$tt8Sf?tF(|R;N8%Z;}C4{19&Y zL<@N*M}r3P7H@39#2}Fp-usQw)7`7&O&H1dC~oF~=J`fH11a>$fPa}bAc2o;$=g-)0s?#dRz=sOu0}kPvoz1d=I3o33_ArEB0XzC9%jV zpx-rKCNdgSN-m~9U3CCO<@QPxw2Z2=(B0rP5aRZ#o0Jal@OR>?HVmOxXwH65ydSvZ zCY%Qiu`^N5XVp}9@w-2VE}CR!z-t_KPuvs()7&of6+8A1=?&1(X^FJPLeJk9bEO4K zXaSl@$#%SYA0@%;XmI9G>yIm5^SoU>u{wx{!}M~*wPq>Gh7yuMKzm0I094Ft_6iaO}c~5j%~6qxgvR?68))5gTild)=Il3A_6oS+nq* zwC`T=OOE~i*2`6IpCXhD-hsV|KtU`--AKy+-!3YdyH_pjcB>bX?V(n`2drdqFemh< zR~+cmQd|f+{?mL>D<_`@f}f!KX_G(uDyi9zn%|S&PqVJ@(06v7j8-_n8`t|_0U!pg za8DYesiN6%dfSpPh92jFhWEWMIygDxo=zoF%KpH9PIKK(<|!~*Zs?0*UNi_^>UMmA zY4)RF+1>_D{Qm53siWihlwk|SNqp*ega%;jq7|4!9`JrVDWgaq?1*{uU)8g@G4AWQvmO1d@KpDFDb zp{CYZi5&+SC+SnwNGqnfaXO#zKZ|Z#v+U5sct;DiT)mf4X)%r-oD)KoJrFj zXLstCMqbPPPb*`CYY%R}Cq-qVRZG)Nde?NSi>}^(MyVrMTPH=WFT;YFmAD+!^S#k} zK6oNH@ME>rmf7x=qdlIDCb)TjW251kPKKw;Y~v=6ckY7UK{wUQ|Lkm%$QPd+EX3Vi zuF}LkKFoYrA7|RaOxd?=f5q!Ng{?+D@8sEiSZI7OI`^y37kYPoZN^6xkz=_Vduw;q zM-*w>8@_F5d@1Vga&xu0{0s508rb*mGEfo*?s`cmp6m(UF8#@z!+0)p%Y1xLbF;pW zQ)WIj)Z$rsbxP#5pXKoC_MX8CsOlb^AlOgv_UDcJ?S8d+nWrNZy{|*-abDC?bDCt# zG_}(*Uvn~dCW1qD8)cCG^cR;S2xoBl=DlsB*m;OW=A$Q-w0 zjIsm}rU^X9yqr%qw-m0ZAAx+(T8{=|BN7FHcT8hGvGr9v)s%p`-F*+#%kHN4NJ-rN z|M`DW56P}qDhiydM$&jc9!aqJT~z880r7K)xXnbl6sIg}OzEmd+Ei8wH3DVq^W-&b ztlPJMQG=0QT6aKcr7x9Ti{FAk+li<8PK?${I#oXPhUaIt597K0oMaCZco@3J9=*-n zp}vUb(GngHL&EK-lwNc@tv&V;5w%UmAG#x0tACiTsV*=r3Xiq6f!og8&6o8^o$n!% z2Zws@sSzTJ!c@({Y7*KbJd8zUzGmyJUPl}C&|}r1SC`8ufdcJNqH07^Ui@a$Iz)rn~x+EF;cteT)4q6?&|5x zQ~j0ndpsAF3khA+cbZH-;iZvhnQqYo-Jsp~2Y!|=b#jacid&?&@*93e2QwweBdfHP z(HkX=G~C^DIXr8txXW+-pqG><11*(T#ST<^Jk9u4mgk=K9i*SzBJ$M#P@MkE4|&2p>ih-jN;_tVN+_d`1_5AAC| zdGx_R##+~CvS0bJ8#js7pN;i(M6s1zv2?h;giZd*?~0z#-G&d&!EYY77A@CZe8S?i z&fQye$mjn!AFPYuFY32W^gGCqLwtUxL*RFb<-EP~wXRp{SM#qd=w_1BH}RuXtpcg- zlog{cZDXYECnJ2HvCB&566NluFWNZ{CL5JXZ$>{hxp)1#i~z%Q*c~Hjci5Mt_-FJh zO+IDzjPG+zJH{}BE18QtpogQy)Gx)tH_!*GrF`RDnh0nzJm@Wsa1+y6Xf0xlHE?8^T& zPZS-DC@y54)dRCM4x1_K=8|TAqej&59$y(!D{S<7qDDD#n0>o`nC9aKw#e>Ynpb(! zj^LxmIK?)JyQpFw?*8Jb4#K?r2E8PRfSa0@%fEZ1L|5A?}S4(6yBtak0b-%olJ<1oOkkYTWoaRhEeZLXH5gx}X8T z0-*nv-2Y$eW}r$)X0?>pPHO{WC6GDbPQjn=-=ILEen^>{fQab!9+3IRqkx-+Q;VQ{ zZf}6v7*IHncy~67pb--W4hxmQ3yXMGG(yzUf7CitJ3wKD)ofV8SthAtSJIyw+&jR;rS<@~^O zli)s1%VAwnSxmvJ!xck8&y%?>k{l^x!=lkXu~P$~H;dNBmr04f#AVGFR#6GKrdnlF z**UNG2jyvXf&_MeWXDnx#h!pU83iakjgHQ zGG^n8cYu?h_lJCJGpoE*IpQzkFNf;XKP;gIs+y|gU!iYk;|&~Vx|mxIw`vobkA66& z)jH=Sstq&8PoMrQvX5d{`c=3v%i=?ds8$^QR+DzPX$@>C*r2nl1nAs(?CNBK_ie7T zgDiy?ZduxH<*|aTRw7@1Q4u@0PL!>J0OfI5m5u|#*R~rA1XD0UYpUz!njNefdrmNc zO$95{^6N`CvW=pQk(?%#d8JbFyXat}5=?YXoj3!#U{g@U?j6u;0P6Ez`8gvJ(OB3U zarRUBx822ff$PKsLpSw1p4<|9h050vdkWs@U>V943KeIzdPXovGGeG1%57A=*}?(y zjY;a^Q4Gkj>8cb!lm24*@PyQW5^npD=*b%-=!pqO~hV}T0e==iLCeY!S0Suq4z|Z&W3Fs|& zGHNUP71Q$pd13%sCiefO$ykKMjFl<#M4XsO|aHpOvZDVP{d8RFX>!c zy;DZZnJ!O5SzFZ$Z7zq7*@23N_y$eJVdh zyXUZf6h5`ytYoy_j0r;6aQ2o*Tf%c3eMYCMON@_=SS$5y3%M%!lf(cB4p$i!#tMiO z^hltcVe=U474Ss!4+xlD4qfeS2ybM~<>qSKAHLmlH4|2iHx!@*6zPcbALvR(8I61u zq9!0C1^u?_4cnreBy>~a5;>(vr_!c^vIpSac0bG(9Of&CC#Jc-h z*G4L{Y!El!0YxfFBfZGRcV-#@NbwXH_}21Fp|s zK_L#`bD{0wP7nND0c8X1@?;eY1gcV2QjZh$+?sf`(E~S^mzS@(`_~J8gZi86_yEpI z(o+GAG#~v_yfFmG%fjy|nkQ2kfGpDqis7ji9=gt!KqpTbm64$oVy}V6^yejvMQ^Dg z09!(=f88`l_TMr{U9={*tCN!xfGtA4sONuk-JQMudtVCoQyVZ3Kw5%4JajF!66rrq z+xzDtmoo=fW$$@Kb74OI4!4~`*?M_h^(68-|9Qe@!e-89%jO6spl&@@_}vYpg&^La zeI$Lg&dA$CatXbn>A)L08kL^g)#Fg3KLmf<)?9-(()aOZm*bCVwI66dHS;+c)iajW z!f95F;z(^8@-;NOhsfZerKXOJ9mI0NRY`q)&VP+df=%)rEMu|pLy#3d(+m?)OYI8m z@&aan0=fw z1(b9G>Do8(g5PC6*WpAyXK#k{Zqb}I9C3&6g!UZOKgo`$D?;~2s=ln4V*_~ zm5NViYJ!27Tb{VmjW_rKNKX<;@~cXcd151KyUs|G%68~-%qOfK1JuqWxw@sX{!^i?Y#&5{hNe$u)U1=Oz) z1E`WX$#dtAoBJoxwgo@xMAy|XQat*L!Hq&855A~F=flE8NT z_(h#+yEIZ!rQhrs3Sov7ZKZVb!}l(5@2sV_l8`_dpU^w7Yww_&oA(L21cfZgBl$1! z*wg&dcMycQ+^G%9WYZFm6Bn4d72CPNe1-yLM;^Jnv&|T9P|S?mv(O8c0Xa$M!NaJM z1EMN?O#0zZkpvL+%D<|hA^&}}&ODV8g`kSmp%QX{dK6970VG-gi9#NnWC=YSSZ-eV zKh2{GM@HJN))2=g?{+GZ-jvo%Bi%ei-zuX>DxaF|qd4e5!Z`n^riuXp8MKq{qb^O% z-|lCrJat-f8FO|*cT@hR{muoLO6z%!SHS;S`G55DuYRCLEV}VA<^Qnd#e#pU z*fjZ9M`C`d^QKGK67c~xko6t(NfrxzXAaPibpzzG=<@*4&FS*Cqa_$Np!Z6|B`dy zp~(onbN)FB>n200J0y@ip`B9hv2XC*f5{)Ih#3KS<}HjgkpSPT5Wxh%67TCz84ODJ6HMC>iO>;iQPelE#FDgjidOZ_}mI z0g}hEM*u*SS#WnQTOsZ>fa~*%3wJI;y9Bj=Y4>SQYOf$EgabCCgg?VX?EX)lkqZ-4 zDJ8ctAXp$sHcx}; z77kMPF(_9f&HHJNSEK0Vs}kq^+*)yL~W(HJu%>sI7h2z zQ_KuTLHz;7XhhxKcE7_V_}xKbwiAW;rXD5%O77yu4&ucIj`2iTl)(`vg?w>osYtwuUC6yB8DXl+{Qg| z!V4wJuBU#_U3vyR?biY{)BIxb_LC{^a@P!*YD>4~u6XW<8}q8c%}V@=dhJbO3ve!X zhZGQNUfL@-;msfWS$hV1~4ggEzHd(C}L=4Q2SOK#IA&zEw@ zy2XG_^mptb^?ePc^*`qLw%dZ9{055hfS4PIza!eHfMWmVHDOAP^-Y`WqjPU?pNtjNOE5k^`K&fVU*>#$KEy$%CO zFRgjrs*jGWSEdkn+#3!hWabvbpoQYqkD(F{CQU@?tr5UvnuA^SLsd-e&aOskf6l=) z`?kRsh2EQp#bY{*pncM~jr(rC&oa|_>WhVHIGciTdom*iVvpERA6R|6R&-I~89p8X zwQqjW85i$6u2lFOM#9ZNDQdLn51sk*F99Ca@m2e#Gj!d?F(@i$I8W8Mo+)vHWgK83v?BPBY(CEK#Z z`AsQZZn6xzvZ$OkS5I^4A?$FBBel0H`-{H4OO;c&jo}Ni*$3o~OiopaO-{WJI6v}z zeeFSC%`S=jxyV0D1?Rl^@HPbcQAbI&0}=DkVGb#R7SA&nVrSjxw8~I9XZ~$==*yU@ z+kL?-HoH|~muU-YEwzM)b=i4f8GC*cgyix>+B|X#6NDhIjG}+8`M23IOs#NzG#>h= z7e$+y7)OYWi)hMw3+5z^*gjkRM*1j|Xh^D=dMYbgfMB|F_BD;H^_F*Sq|F}CjTUnA z{<-R@EG@+YG!H8^My@Os1|PGh1y?$`aA<1wf5+7E3ZENFcTiH3=E^Si2aVi;tC}fO zh1b*Y{v+t#7@H@*;md<4ZB6bc)vq7Min!=0Xlp)w|1@+$Vf)K_F#(x+lRUnvoGapM zjO~<4lugV&iXtG)Ou^Ht?IN@*S3|p(w486}v3f~pa)>Rb=?@(iEpi2c2aO$51n6Ai zv=@04C}xEshV69eaElZWvw3y;d^|r#g`I~SoYWo{3?F?)4$iU?4;NxfiOSh%#udj| z)8G68_4{qzI1^(tJs-l&l_&Dewd87PFpgW3P0^n+*~~K2l_}&GA(`|tp#xXCqAT^rPJN}WU7m`Q$15ncmAv}{+B0Jv>ac+bFHV=FZtEBZJK{1T_b}&ICp5?DX>hhowA-Y zZV1y*)M{3kKn<3K_}O^B^jkJEsC=30@s+@VI{NBETepI8S4*@7eEF90;b$R6MfR+r zKdO-h_zaZyj21cdkOd+sYoj@??|5gXN5vweVxPKy^-TA&v1AZjL>@u24$U_(hpaXH zTo9?ap#N3NmB&N*wSASP5(?R-L`IS=dy%C?))bN4_gv@PEB5b@nY)4O z>v-v!da)4_dY`L!MI>sYv$N3PG<)UU??&jj%-70XR>lTDLKi;EB%2};>iaC05qY-H zpJqM7)OlFR>e*u_@Uf5m5s#Z764x@c%e z1$L%v_l`Xjn0QZqER-W9Z|*9eW9(#;*u3@h=jZRfF`74cmGT}!wljEOi)G+8??m^6 z-q#gvu6afVf3bwso~eE;aH?)vv?&T7Gooh$%Z(#Kx}b)-f2r_hy~}mrCr$w0j72wE zG$!a23FGRy=wGxW>HK>1bggHltcAoQ3$zWEUR>_w>Q4AJ^xd`hiBYPkHSQZ=hH$fEG-5e*(nHbE-M!Xb zCOe}lRG+`|5G#nlTu;UXDmWj=KaVZ|yF>eONwESbM$x2!=(GPCJX*z-?uYPAX!V8~ zxK0)~MdhF6=rrmnY8^NmLKiy&Hod@iM>Ek|DjfySMO1`x;+S}INnUXyOoT;nbPAe#oKSSKH=-C+h<&zIN!OOhazr0M~( z_G4>nE6A|{kKxJZM8WA=P=W(Uks`O@=F}F8ZE(Qr)`Xg>fR9-jr<1>9Xr{-0c11A_ zP9(jtNIq4$J-`EzB1AT|bs*jP&4~u4_iypwN6C9+r;*!NWwX$1X0i}@9>CW{zluwo zI5)K=p&bFCuwLvdzWaE;21sPH`>7D?F}EuEO~;z<*ctAUoE zckRxVY+3IBOyJZ+Hfqt}%DjNrg*v=X64P0TG(OZ*o0Kt{%pDUgj{4BJ$BW*(?A(D= z*228R*=&_3u6WXkRR!;Wd037h>-+LCw{Yh9(Tb0XYKg5P!s<}DbE}te*>3Pkb=3ZE zwHbrE(O581n{!FFn$53YYsH*s)}y*<5$PSzhz=rIj3{1tQ%A zJz7=lSv6i^-yBlLXCJ=y?YUXP3{q!KB;hfb}QaO`%~9 z4(SnEWWvQv!-U!BlGv^{ye=#k*$H!5JL4~{55Fh^(_@T^@7SzqET9LG9O_7cyp`m~ zjGjc&z-`}{*nx_)iv|uKX9G{1gOqYn-epR+P2}&AVvl{ps_&b)T6=Q za6`5$pp)$ZDHyvxdPcb94IatM6ykS0Yfp)75m9~UY@Ns3jXC;vXDBDI?itsj-*o$Y z!0p%+q2b_ia^*p-$@69pdFS@ZV&mt3*s-ywUQRpH72*-pwo-5}UXgC@Fni3N&VhE1noOiv zS418VjqJ?%f~9VIEw*)`hk_bBZ@R<5VSaPfub$pJHN4+5o$CdO(QEY16c%i1xk~wd zFj&ss^8Iq3^J0ze+m1;ixWE_ z3P!TqlZ^Kbi@ZU^qz!ShgydBt4g!#l!27sdT8{If6Cz&Tr#lj((JzsWi$0l9dPU|k zPq|j+*HRf6vJTn|c|)XOm+xLrnC?{wLE9HPhD^k`aA%m`Xrxt}dE#?vuT-HJ< z=+wU;C*}*%@#08~eN5WI6|`Pze}dFcL%{>aqX6@w-91E)(lb~~MU zn+GQiYg*$ap{K7*?(NcL1{&nv5p;mr8cQ&1*$_&Em_1By_uY?0ZvZEITmLAwpFkH$o0({-65z}DA3Lr_23!Pq1|^VChd-dBm|1a@5O za^iGxEWYHdoK}HXe5`_8R)TDkif~$0yW)f6u7z?zMI0-ZI-D5^#KdR=5X;7VtI<)Z6H7D^zl7~nbKPq*LOYe(yyA9Knn;E4$t}Qn< zVlo_5P&#Y5)MdbFFn=SQH8&*GxlU>NR z4Gp3Z%wjx@<%p4ak~30!gF!}F{E?HaO=^~@pMI>o%p}J6W%jphxnQGogZe$q9{gk% z%{7M>Z%eqn5-BO?vs95&g^LU@UV?f*xBh%&vVc*McDarQvdwFq;E-BjcL-xuaSK&! zXhsm$CRvX8P}>7Mo@?EE<<%gzNV}#~#wT{1z2yYHBe2Fo%JHy9xGD+1!mskQ)yzhi zkC+i>s0FL2BZ)+xJ8@ismzR^e7bOYYEwVogMw35aO5 zNk6`@G@XgA8F*yx_)Q!sRPCGTV-0ouD2TvWzUhwa^RgY}2yixRptyXtC)}m=#aF1d%DvMuPKvC<`?R|?E2a{JS96UYr zrM7aFu^yw*6)Sm^G5X_{I5C2GoO@*Ssld&>q(h&gMtx0!q?-1E{9``zZ1b+%&A3rt zK4Mo&E{_dp2Y)f(Mw$Lr(;)$3Z69GcQ-_nl7i9>li5SQ*?wuT}IlZj!JGWU=?skPk zv|Cjc`A2Qd=`I)Z`Q@8)eNXvNioQ(;heOV9f~S>tM~pCb`>ezf=8$f-{J*xC$Bh_< z5$c?S(D?&d8mLoc+ck@31|f9vVw_I*O7=oecm#%n+L3t-M&``nm9Cn7zx~`9JyPCX z^2IRxIf0KTN*!2<5RksMrIh`*r_G~8-K^(IzegW-Q2?3zY7#fzC=2tX2qman%Dz|l zc1{&4*VoscMvO0XK99Z8_;XTGEp1JC6|RO$kH$k1<`sv2C=2yZi#B_xr3ncxNc1;n zAQ$?jdv645uJy54|JulKB`f^hE2S{muAR^CqZzMszamICP5LdgJgK?ygQ)>FdTBV7 zBP1aCWgAJuw-v?Na&tyLy$JFf@*O?&{ zAL@=rq^J{V@<*W+O7}rPvaO%Vm|6J_q7U6U++Y%--VTdmA!iT;<0L`R(itL?#J~*ij`*?u|?&J2x$28!pJBg z2~U9Y(F?(=bBs_3Y2gPpwHCdtZhP$Aybb^qTm_^KWX1sMkB^d869&Nfx@`sNL2iuEalk6`S=<4ukLBYgn~xd!TMrL6yMdD+9jOcvnATOwnHk36 z)8283QF;jlyF@DQj%qT(WR@9GHN2CO-vddR;RyWIYny@@N7j}AtAeUQc}9hdmL!`O zJ|KOSyS^qKI9%!?{GjBvNU@a=AMw-LC~;!MX@4}Cq5hBIsBWZ223UCbzl2{J5x@Ue#(*BDOzpMK<`F?LIh#oQi=@5wW+{eteX+v)j z7jr%SiFw7ENuV{Q2e1&;+cEDFO9GuE{Hi3MrYBkebeO3&a5OJuBWK&E1iHO^6 z4cUf*azyg31h5~pX7k9)6Ob|0gmL_+x6tIcD8(pog2cWTqJE?%9;(@)b>v)!|4L^CB=`)*XqT=)Q;A*o+f%A2w#Ad>rtrUc8H9 zrr6e{IQ*bd&)%=x6yr-vL0`LJ!@m)Gzyp#OcB)h%aC*N01~*QxB3sT+^2Ag|pid7S zbzX3y1n%SBl9gXhOrgbonMN~6>+k``UwTkSI_~&vOS7T=h#l~I#iGz795|*u8<6I7 zVbcMMnf7--#q`Mp60$99CVy@yeCQq>0x3EcYnh62eb( zyWOEKoBT;^pWDHe(-d9oRD<+(zztL@$w@oKGGo(LiIeomW|&yEw!joV80;ZRpUkN z46{_r_L{oNe8%g)wheEmNc-FOe;4f^bAz2>Xt7+{@OKZ=DUh(!5Sl(8U{{Z$n0HkK zektY)Dt`wBxi4uJX_3z+gVoe}zV)}$wLgIG-)-72m_X8=xQ7B<`8okxaFrW1&{O2mg-yE`_9}7Oau9OmiVojx9;4bL7p|~krd^Y- zzD!&7a$YYqx(ti=H;&Ozm6IYQ<7lt z6R2}ppMgK%?mw5LaKIS^R-a*oGS-d&YDm_FZ3(Pp_RX^~UkoaFC=yB{9eo8~`nWua zP|Gc{g;JCR3rP~NgPgh;QmedQ=<8mcX)-Iu`lEz*n{T7?f050I2B zruo6dURhsFh!F2`9HqF&XMeP`iv)E-o#FR{Mgtj2JI)NcTKU)aoPp`h-YNjl*YiMd z-1c)}=9g9p;R|CW>F(t_J#!vBFZ-2|DitbGML&|l0QE`AHW zWi81I5k>);QT7s@8pN}-UsrX*{?Z}Phtx4`V&(hwAy|w!8@pLAGXk^u z7ch^(43!f0i)I8-mIoHAYxH0G`VEZ$A3{hN>poab@AQ)7%> zZcX1#*|_coWxNA>H031d1WQ-iEd}G{o|ejIa+~8uYCf8~z3E@p%diFKx;`C^z*ub8 zoPPJ7#lr0k!@ka3)*9ZW==sK{Xo0AqZiICDLQ=#Cxb0vX@MN9f$e~W0_>#}+J^PWi za0iVMFZjeOL4?j|mPjqyYfCszw%1~%xO%)~$go?va-Wwfv@kPH7%ghgdDZt&alGsr zD7{r7U$7$t?mT|a7q=;;O%?JS)PfIr9VcHtLl`R=e%9y(imZ-~*Kup7p`!gE*ptSH z3w);<`C+M6<{=JydbEC5`-3Wv9IKkYUcvkq4GUx9MwVu}SArybtUNqSRtshkHnGw) z&klj{x#|=(r0V9|ofJh)V|4K<^>_1p&YgPssUv9=R{l{Z-E!fYp3YmTXslG@eph;$ zn5rVC&t^W7vMxD@z+ry~r5~Ffc>2)b zUrFs*IN$lfrz$IRAH&1LKXALl2DYYw9MM2--ur?dPG5hf-Yu!Lzo{8SU(zVs6hWnoNV`oc!03y zpyCeK4-aJ>(GI%7X#23eFqXOOn8$HV zx}=0(8=S@T9MY#b6s7|wkq!qA`2;ydnL%znIZahc`GCKPqzVfcgxI{m*!?d?2#D0XaVB1xKG3WmlJeen`dxJJyU(gcd~{6&BOLG zh%pe$3Da*w4&QBjsxM(tcBIhEAxhMyVy-WNWKH{t4cL@Fdg+ZyR%d~IRAMB(O)0%f z7G3!k;3k^QB4l>^I2Sp=a0V9u@c9@&*?s2 z!<6wRc>$~q{&eMA!KV#Xmkz@QpaB0rN-?GMEY8w{1=)T3Tu{(ILI3X;5+irQU7r1q z_ARv5=76fxuV1Z>ve3|!oZ5;r2X)p*`xDhxaCU=l^>5A_#;3M_3hs1kF_+p+^NGPk zP{-m~lY`fLFscPmCU@n|S$!ejPohdelfWj#yh5)9ThB^6^|Sa*Y*}9PXYsj3@anGc zEhwDIH9e_%P5oA`ytSl&b`ax9{l~cHf?s!VeO+cfrnp3Y+Jd*#f6*ps4LXh@2s fQLUa^*(BH~`0njVTrN0BK%=F8`ee?Divj-w;D7Gs literal 0 HcmV?d00001 diff --git a/pkg/util/excel/options.go b/pkg/util/excel/options.go new file mode 100644 index 0000000..87cf606 --- /dev/null +++ b/pkg/util/excel/options.go @@ -0,0 +1,27 @@ +// Package excel ----------------------------- +// @file : options.go +// @author : JJXu +// @contact : wavingBear@163.com +// @time : 2022/12/19 12:41:40 +// ------------------------------------------- +package excel + +import ( + "fmt" + "path/filepath" + "strings" + "time" +) + +type Option func(excel *Excel) + +//func AddSaveFileSuffixWithUnixTime(excel *Excel) { +// excel.SaveName +// fmt.Sprintf("%v", time.Now().Unix()) +//} +// 时间戳作为文件后缀 +func OptionFileNameSuffixWithUnixTime(excel *Excel) { + ext := filepath.Ext(excel.SaveName) + name := strings.Split(excel.SaveName, ext)[0] + excel.SaveName = fmt.Sprintf("%s_%v%s", name, time.Now().Unix(), ext) +} diff --git a/pkg/util/excel/readme.md b/pkg/util/excel/readme.md new file mode 100644 index 0000000..d8a18d6 --- /dev/null +++ b/pkg/util/excel/readme.md @@ -0,0 +1,161 @@ +# excel文件生成器 +## excel文件生成器简介 +此模块提供了通过`.xlsx`格式的excel模板文件,创建文件并自动添加数据的方法。 +此模块通过设计模式,在"github.com/xuri/excelize/v2"的基础上封装了`ExcelCreator`这一泛型的方法。适用于快速开发报表导出功能。 + +### 基本使用示例 +```go +package main +import ( + "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/utils/excel" + "github.com/flipped-aurora/gin-vue-admin/server/utils/simpletime" + "sync" + "time" +) + +//读写锁 +var exportEpidemicPreventStaticStaticLock sync.RWMutex + +//定义sheet1数据结构 +type EpidemicPreventStaticReport struct { + Xid int `json:"xid" form:"xid" db:"column:xid;comment:序号"` + Time string `json:"time" form:"time" db:"column:time;comment:日期"` + Name string `json:"name" form:"name" db:"column:name;comment:项目名称"` + Code string `json:"code" form:"code" db:"column:code;comment:监督备案号"` + Street string `json:"street" form:"street" db:"column:street;comment:街道"` + State string `json:"state" form:"state" db:"column:state;comment:状态:未完成、已完成"` +} + +//导出数据 +func (m EpidemicPreventStaticReport) WriteToExcel(datas []EpidemicPreventStaticReport) (path string, filename string, err error) { + defer exportEpidemicPreventStaticStaticLock.Unlock() + //实例化sheet1,载入数据 + st1 := excel.NewSheet("Sheet1", datas) + var suffixName = func() string { + return fmt.Sprintf("%v", simpletime.TimeToString(time.Now(), simpletime.TimeFormat.NoSpacer_YMDhms)) + } + //实例化excelCreator + exc, err := excel.NewExcelCreator("防疫日报日完成项目数统计.xlsx", &suffixName, "uploads/template2/file", "uploads/template2/防疫日报日完成项目数统计.xlsx", st1) + if err != nil { + fmt.Println(err.error()) + } + exportEpidemicPreventStaticStaticLock.Lock() + // 导出数据 + return exc.WriteToExcel() +} + +func main(){ + var ( + reportData []EpidemicPreventStaticReport + excel EpidemicPreventStaticReport + ) + + //do something + //.. + //.. + + path,filename,err:=excel.WriteToExcel(reportData) + if err!=nil{ + fmt.Println(err.Error()) + }else{ + fmt.Printf("path:%v\nfilename:%v\n",path,filename) + } +} +``` + +## 使用说明 +此模块的使用流程如下 +### 1. 创建excel模板,定义好工作簿名称和表头 +请注意,程序默认数据都是一行行连续的。如果两条数据之间间隔了一个或多个空行,导出的数据可能会出现错误。 +![img.png](img.png) +### 2. 定义工作簿数据结构 +注意事项: +1. 结构体字段顺序要与工作簿的表头顺序一一对应、命名规则随意; +2. json标签用于数据的转换,同时也便于将结构体直接作为接口请求参数来使用;没有json标签时,数据将会被忽略; +```go +type Sheet1 struct { + Xid int `json:"xid"` + Name string `json:"name"` + Age int `json:"age"` +} +``` +### 3. 准备数据,实例化sheet对象 +数据类型至此结构体和切片
+`NewSheet`方法的第一个参数是工作簿名称,需要与模板文件中的对应,不然导出数据时会报错 +```go +//准备数据 +var sheet1Data = []Sheet1Define{ +{1, "张三", 16}, +{2, "黑猫警长", 18}, +} +//实例化sheet对象 +var sheet1 = excel.NewSheet("Sheet1", &sheet1Data) +``` +### 4 生成excel文件 +### 4.1 直接生成文件 +如果你不需要什么额外操作,只想直接生成excel文件,那么可以直接调用这个方法 +```go +//定义后缀名生成器,如果不需要可以传nil +var suffixFunc = func() string { return fmt.Sprintf("%v", time.Now().Unix()) } +//导出文件 +path,err:=excel.WriteToExcel("demo.xlsx", &suffixFunc, "./", "./demo.xlsx", sheet_1) +``` +#### 生成效果: +![img_1.png](img_1.png) + +### 4.2 `ExcelCreator` +ExcelCreator可以用来对工作簿和工作簿中的数据进行增删改查,以及生成文件。 +#### 4.2.1 实例化`ExcelCreator` +```go + +var suffixFunc = func() string { return fmt.Sprintf("%v", time.Now().Unix()) } //文件后缀名生成方法 +exCreator,err := excel.NewExcel("demo.xlsx", &suffixFunc, "./", "./demo.xlsx", sheet_1) +if err!=nil{ + fmt.Println(err.Error()) +} +//可连续添加多个工作簿或者不传,当然也支持一张工作簿数据的多次传入 +//exCreator,err := excel.NewExcel("demo.xlsx", &suffixFunc, "./", "./demo.xlsx", sheet_1,sheet_2,sheet_3) +//exCreator,err := excel.NewExcel("demo.xlsx", &suffixFunc, "./", "./demo.xlsx") +//exCreator,err := excel.NewExcel("demo.xlsx", &suffixFunc, "./", "./demo.xlsx", sheet_1_1,sheet_1_2,sheet_1_3) + +//不使用文件后缀名 +//exCreator,err := excel.NewExcelCreator("demo.xlsx", nil, "./", "./demo.xlsx", sheet_1) +//exCreator,err := excel.NewExcelCreatorWithoutSuffix("demo.xlsx","./", "./demo.xlsx", sheet_1) +``` +#### `FileName` 和 `filesSuffix`参数的说明 +当设置了`filesSuffix`参数后,创建excel文件时会自动生成文件后缀名。 +如`FileName`设置为"demo.xlsx"、`filesSuffix`返回值为"20200723",那么最终文件名为"demo20200723.xlsx" + +### 4.2.2 新增工作簿 +如果工作簿已存在,数据会组合而不是覆盖 +```go +var err = exCreator.SheetsAdd(sheet_2) +``` +### 4.2.3 删除工作簿 +```go +var err = exCreator.SheetsDelete(sheet_2.SheetName()) +``` + +### 4.2.5 取出工作簿map和缓存数据 +工作簿存储在`ExcelCreator.Sheets`中,存储结构为`map[string][]sheet`。string即工作簿名称。取出后可以进行任意的操作 +```go +var sheets = exCreator.Sheets +datas:= sheets["Sheet1"].GetDatas +//do somthing... +``` + +### 4.2.6 将数据生成到文件 +```go +path,fileName, err := exCreator.WriteToExcel() +if err != nil { + fmt.Println(err.Error()) +} else { + fmt.Println(path) + fmt.Println(fileName) +} +``` + +## 相关问题 +### 1. 为什么不支持通过代码直接生成表头? +鉴于在部分场景下,表头格式比较复杂,且通过代码生成灵活较度差,单元格格式通过代码设置比较繁琐。相比较下,没有直接创建excel文件模板来调整更为直观方便,所以没做此方面的设计。 diff --git a/pkg/util/excel/utils.go b/pkg/util/excel/utils.go new file mode 100644 index 0000000..ca93311 --- /dev/null +++ b/pkg/util/excel/utils.go @@ -0,0 +1,41 @@ +// Package utils ----------------------------- +// @file : excelHelper.go +// @author : JJXu +// @contact : wavingbear@163.com +// @time : 2022/6/9 13:41 +// ------------------------------------------- +package excel + +import ( + "fmt" + "strconv" +) + +//行列坐标值转换为excel的坐标。注意row和columnCount的初始值都是1 +func GetCellIndex(row int, columnCount int) string { + var column = getColumnIndex(columnCount) + return fmt.Sprintf("%s%d", column, row) +} + +//获取excel的列索引 +var columnIndices = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"} + +func getColumnIndex(num int) string { + num-- + var column = columnIndices[num%26] + for num = num / 26; num > 0; num = num / 26 { + column = columnIndices[(num-1)%26] + column + num-- + } + return column +} + +func Int[T int | int | uint8 | uint32 | uint64 | int32 | int64](value string) T { + v, _ := strconv.Atoi(value) + return T(v) +} + +func Float[T float64 | float32](value string) T { + v, _ := strconv.ParseFloat(value, 64) + return T(v) +}