Compare commits

...

53 Commits

Author SHA1 Message Date
lzh
4098d590bf Merge branch 'dev-lzh-1009' 2025-10-10 10:35:31 +08:00
lzh
db58773aea 新增使用类型字段 2025-10-10 10:29:46 +08:00
b2a96ad465 Merge branch 'feature-costType-daiyb' 2025-10-10 09:18:42 +08:00
6702083fd6 修改字段 2025-10-10 09:11:30 +08:00
5da9d19112 Merge branch 'main' of http://gitea.tools.fontree.cn:3000/fiee/fonchain-fiee 2025-10-10 08:54:15 +08:00
lzh
87d46af603 更新proto文件 2025-10-09 17:11:33 +08:00
15072f9e49 导出 2025-10-09 14:45:16 +08:00
sxy
c72d45c020 修改图片传入类型判断 2025-09-28 20:46:27 +08:00
sxy
f3c5cbff2a 导入修改-1 2025-09-28 19:17:19 +08:00
ebe59394cf Merge branch 'feature-tiktok-daiyb' 2025-09-28 19:14:10 +08:00
5746dab3e0 添加更新视频的过滤字段 2025-09-28 19:13:58 +08:00
6c4aeb8193 Merge branch 'feature-tiktok-daiyb' 2025-09-28 17:55:32 +08:00
7200099204 更新字段 2025-09-28 17:55:17 +08:00
c70c5913d4 Update README.md 2025-09-28 15:09:42 +08:00
JNG
414735bc54 Merge branch 'jng-0924' 2025-09-28 15:09:08 +08:00
JNG
3f5e4e3bc8 Update reconciliation.go 2025-09-28 15:08:45 +08:00
JNG
af4c370b84 Merge branch 'jng-0924' 2025-09-28 14:42:13 +08:00
JNG
9f7e2d1667 Update reconciliation.go 2025-09-28 14:41:24 +08:00
lzh
557739aeff 更新proto 2025-09-28 14:17:50 +08:00
sxy
91e1dfa196 导入修改 2025-09-28 10:53:18 +08:00
2825ca826e 合并正式 2025-09-28 10:40:08 +08:00
90d088101c 重新发布不扣除 2025-09-26 15:19:31 +08:00
24cc34462e 分类 2025-09-26 12:28:27 +08:00
9b7fbe73fd Update media.go 2025-09-26 10:54:01 +08:00
1c6172a848 更新proto 2025-09-26 09:25:09 +08:00
af7fa93b4d 修改同步 2025-09-25 16:41:08 +08:00
48dea8eaac 是否授权 2025-09-25 15:56:33 +08:00
sxy
761b9b656b Merge branch 'feat-sxy-import919' 2025-09-25 13:47:13 +08:00
sxy
e2d87cb5e0 导入修改 2025-09-25 13:46:27 +08:00
6a6bd459e6 Update conf.ini 2025-09-25 11:12:59 +08:00
47473df3be Update work.go 2025-09-24 17:16:10 +08:00
fd908ec72b Update work.go 2025-09-24 16:49:48 +08:00
JNG
6e6f159b23 Update reconciliation.go 2025-09-24 16:07:23 +08:00
97f79df433 Update media.go 2025-09-24 11:58:22 +08:00
e037f91507 Update media.go 2025-09-24 11:56:42 +08:00
b35278771e 跳转 2025-09-24 11:47:16 +08:00
38e7e1aac6 fix: 自动刷订单的,过期时间10年变1年 2025-09-23 16:20:21 +08:00
sxy
e33aaac2b9 导入日志shuhcu 2025-09-22 18:31:51 +08:00
sxy
430304cd1e 导入修改url-1 2025-09-22 18:27:00 +08:00
sxy
b4520ccf94 导入修改url 2025-09-22 17:03:18 +08:00
sxy
74b5c28cc4 修改配置文件-1 2025-09-22 16:42:08 +08:00
4dba6c7669 视频封面 2025-09-22 16:41:40 +08:00
8271683614 添加信息查询 2025-09-22 16:29:38 +08:00
sxy
26a4fe0413 修改配置文件 2025-09-22 16:27:38 +08:00
sxy
be7e522219 导入不删除源文件 2025-09-22 15:40:52 +08:00
sxy
fcb33cc715 fiee第二次导入修改 2025-09-22 13:37:31 +08:00
sxy
3cb95a8f65 fiee第二次空指针修改 2025-09-22 11:47:55 +08:00
sxy
5dc0b1cbf5 fiee第二次导入测试-2 2025-09-22 11:42:06 +08:00
sxy
0f6e0abf33 fiee第二次导入测试-1 2025-09-22 11:40:27 +08:00
sxy
3d13747ad9 fiee第二次导入测试 2025-09-22 11:27:29 +08:00
39ca08605e 视频发布 2025-09-22 11:12:19 +08:00
b4dd5d0778 完成视频发布 2025-09-22 10:02:44 +08:00
6d34d7d4f2 复制隐私文件 2025-09-03 11:48:40 +08:00
27 changed files with 3568 additions and 1463 deletions

View File

@ -0,0 +1,13 @@
## 测试服后台
http://172.16.100.99:9028/
13580848136
https://erp.fiee.com/older_list
13580848136
Aa.123456
测试h5
## https://saas-test.szjixun.cn
正式h5
https://saas.fiee.com

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.29.1
// protoc v3.20.3
// protoc v3.12.4
// source: pb/bundle.proto
package bundle
@ -6139,7 +6139,7 @@ type GetUsedRecordListReq struct {
Account string `protobuf:"bytes,3,opt,name=account,proto3" json:"account,omitempty"`
Platform int32 `protobuf:"varint,4,opt,name=platform,proto3" json:"platform,omitempty"`
Type int32 `protobuf:"varint,5,opt,name=type,proto3" json:"type,omitempty"`
Title string `protobuf:"bytes,6,opt,name=title,proto3" json:"title,omitempty"`
WorkTitle string `protobuf:"bytes,6,opt,name=workTitle,proto3" json:"workTitle,omitempty"`
SubmitTimeStart int64 `protobuf:"varint,7,opt,name=submitTimeStart,proto3" json:"submitTimeStart,omitempty"`
SubmitTimeEnd int64 `protobuf:"varint,8,opt,name=submitTimeEnd,proto3" json:"submitTimeEnd,omitempty"`
Page int32 `protobuf:"varint,9,opt,name=page,proto3" json:"page,omitempty"`
@ -6213,9 +6213,9 @@ func (x *GetUsedRecordListReq) GetType() int32 {
return 0
}
func (x *GetUsedRecordListReq) GetTitle() string {
func (x *GetUsedRecordListReq) GetWorkTitle() string {
if x != nil {
return x.Title
return x.WorkTitle
}
return ""
}
@ -6323,6 +6323,7 @@ type WorkCastItem struct {
OperatorName string `protobuf:"bytes,13,opt,name=operatorName,proto3" json:"operatorName,omitempty"` // 操作人名称
OperatorPhone string `protobuf:"bytes,14,opt,name=operatorPhone,proto3" json:"operatorPhone,omitempty"` // 操作人手机号
Status uint32 `protobuf:"varint,15,opt,name=status,proto3" json:"status,omitempty"` // 1 有效 2 失效
CostType uint32 `protobuf:"varint,16,opt,name=costType,proto3" json:"costType,omitempty"`
}
func (x *WorkCastItem) Reset() {
@ -6462,6 +6463,13 @@ func (x *WorkCastItem) GetStatus() uint32 {
return 0
}
func (x *WorkCastItem) GetCostType() uint32 {
if x != nil {
return x.CostType
}
return 0
}
type GetImageWorkDetailReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -6771,6 +6779,7 @@ type WorkItem struct {
CreatedAt int64 `protobuf:"varint,11,opt,name=createdAt,proto3" json:"createdAt,omitempty"` // 提交时间
ArtistName string `protobuf:"bytes,12,opt,name=artistName,proto3" json:"artistName,omitempty"`
ArtistUuid string `protobuf:"bytes,13,opt,name=artistUuid,proto3" json:"artistUuid,omitempty"`
CostType uint32 `protobuf:"varint,14,opt,name=costType,proto3" json:"costType,omitempty"`
}
func (x *WorkItem) Reset() {
@ -6896,6 +6905,13 @@ func (x *WorkItem) GetArtistUuid() string {
return ""
}
func (x *WorkItem) GetCostType() uint32 {
if x != nil {
return x.CostType
}
return 0
}
type ToBeComfirmedWorksResp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -8890,7 +8906,7 @@ var file_pb_bundle_proto_rawDesc = []byte{
0x63, 0x6b, 0x73, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52,
0x14, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x63, 0x6b, 0x73, 0x4e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x16, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x42, 0x75, 0x6e, 0x64,
0x6c, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0xa6, 0x02,
0x6c, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0xae, 0x02,
0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70,
@ -8900,107 +8916,111 @@ var file_pb_bundle_proto_rawDesc = []byte{
0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x04, 0x20, 0x01,
0x28, 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04,
0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74,
0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74,
0x12, 0x24, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x45, 0x6e,
0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54,
0x69, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x09,
0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61,
0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61,
0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x57, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
0x64, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12,
0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x43, 0x61, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22,
0xf4, 0x03, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x43, 0x61, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d,
0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64,
0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x50, 0x68, 0x6f,
0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x22,
0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x05,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f,
0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64,
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x55, 0x75,
0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65,
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x64,
0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
0x6d, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d,
0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e,
0x61, 0x6d, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x41, 0x63, 0x63,
0x49, 0x44, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x41, 0x63, 0x63, 0x49, 0x44, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69,
0x74, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x54,
0x69, 0x74, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69,
0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74,
0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72,
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x6f, 0x72, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x49, 0x6d, 0x61,
0x67, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x12,
0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x2f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x56, 0x65,
0x64, 0x69, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71,
0x12, 0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74,
0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52,
0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63,
0x6f, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
0x6f, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x69, 0x64, 0x65, 0x6f,
0x55, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x69, 0x64, 0x65, 0x6f,
0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
0x74, 0x22, 0x6d, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x56, 0x65, 0x64, 0x69, 0x6f, 0x65, 0x57, 0x6f,
0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06,
0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x6f,
0x72, 0x6b, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72,
0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
0x22, 0x67, 0x0a, 0x15, 0x54, 0x6f, 0x42, 0x65, 0x43, 0x6f, 0x6d, 0x66, 0x69, 0x72, 0x6d, 0x65,
0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74,
0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61,
0x72, 0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a,
0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x9a, 0x03, 0x0a, 0x08, 0x77, 0x6f,
0x72, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f,
0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f,
0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x61,
0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x06, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x28,
0x0a, 0x0f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72,
0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54,
0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x6d,
0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0d, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x12,
0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61,
0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x57,
0x0a, 0x15, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c,
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x28, 0x0a,
0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x75,
0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x43, 0x61, 0x73, 0x74, 0x49, 0x74, 0x65,
0x6d, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x90, 0x04, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b,
0x43, 0x61, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69,
0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x72,
0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69,
0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x72,
0x74, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x72, 0x74, 0x69,
0x73, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61,
0x72, 0x74, 0x69, 0x73, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f,
0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f,
0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x61,
0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x77, 0x6f,
0x72, 0x6b, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f,
0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a,
0x77, 0x6f, 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c,
0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
0x72, 0x6b, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x75,
0x6e, 0x64, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x75,
0x6e, 0x64, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x6c,
0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x41, 0x63, 0x63, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x18,
0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x41, 0x63, 0x63, 0x55,
0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72,
0x6d, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74,
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x72, 0x74, 0x69,
0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74,
0x55, 0x75, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x72, 0x74, 0x69,
0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x22, 0x76, 0x0a, 0x16, 0x54, 0x6f, 0x42, 0x65, 0x43, 0x6f,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x41, 0x63, 0x63, 0x49, 0x44, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x41, 0x63, 0x63, 0x49, 0x44, 0x73, 0x12, 0x1c,
0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a,
0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c,
0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x24, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x68, 0x6f, 0x6e,
0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f,
0x72, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a,
0x0a, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x15, 0x47, 0x65,
0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c,
0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x2f, 0x0a, 0x15, 0x47,
0x65, 0x74, 0x56, 0x65, 0x64, 0x69, 0x6f, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69,
0x6c, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x86, 0x01, 0x0a,
0x16, 0x47, 0x65, 0x74, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74,
0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12,
0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x76,
0x69, 0x64, 0x65, 0x6f, 0x55, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76,
0x69, 0x64, 0x65, 0x6f, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x6d, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x56, 0x65, 0x64, 0x69,
0x6f, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70,
0x12, 0x16, 0x0a, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6d, 0x61, 0x67,
0x65, 0x55, 0x72, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x61,
0x67, 0x65, 0x55, 0x72, 0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x64, 0x41, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x41, 0x74, 0x22, 0x67, 0x0a, 0x15, 0x54, 0x6f, 0x42, 0x65, 0x43, 0x6f, 0x6d, 0x66,
0x69, 0x72, 0x6d, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a,
0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a,
0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x61, 0x67,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0xb6, 0x03,
0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x1a,
0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69,
0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x77, 0x6f,
0x72, 0x6b, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x1e,
0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20,
0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x64, 0x73, 0x18, 0x07, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x49, 0x64, 0x73,
0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x4e, 0x61, 0x6d, 0x65, 0x73,
0x12, 0x28, 0x0a, 0x0f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x41, 0x63, 0x63, 0x55, 0x73, 0x65, 0x72,
0x49, 0x64, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x41, 0x63, 0x63, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52,
0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72,
0x74, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x72,
0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x61, 0x72, 0x74, 0x69, 0x73, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f,
0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6f,
0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x76, 0x0a, 0x16, 0x54, 0x6f, 0x42, 0x65, 0x43, 0x6f,
0x6d, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70,
0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66,

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions:
// - protoc-gen-go-triple v1.0.8
// - protoc v3.20.3
// - protoc v3.12.4
// source: pb/bundle.proto
package bundle
@ -50,11 +50,11 @@ type BundleClient interface {
OrderRecordsListV2(ctx context.Context, in *OrderRecordsRequestV2, opts ...grpc_go.CallOption) (*OrderRecordsResponseV2, common.ErrorWithAttachment)
OrderListByOrderNo(ctx context.Context, in *OrderInfoByOrderNoRequest, opts ...grpc_go.CallOption) (*OrderInfoByOrderNoResp, common.ErrorWithAttachment)
OnlyAddValueListByOrderNo(ctx context.Context, in *OnlyAddValueListByOrderNoRequest, opts ...grpc_go.CallOption) (*OnlyAddValueListByOrderNoResp, common.ErrorWithAttachment)
//增值套餐
// 增值套餐
CreateValueAddBundle(ctx context.Context, in *CreateValueAddBundleRequest, opts ...grpc_go.CallOption) (*CreateValueAddBundleResponse, common.ErrorWithAttachment)
ValueAddBundleList(ctx context.Context, in *ValueAddBundleListRequest, opts ...grpc_go.CallOption) (*ValueAddBundleListResponse, common.ErrorWithAttachment)
ValueAddBundleDetail(ctx context.Context, in *ValueAddBundleDetailRequest, opts ...grpc_go.CallOption) (*ValueAddBundleDetailResponse, common.ErrorWithAttachment)
//新增值服务
// 新增值服务
SaveValueAddService(ctx context.Context, in *ValueAddServiceLang, opts ...grpc_go.CallOption) (*SaveResponse, common.ErrorWithAttachment)
ValueAddServiceList(ctx context.Context, in *ValueAddServiceListRequest, opts ...grpc_go.CallOption) (*ValueAddServiceListResponse, common.ErrorWithAttachment)
ValueAddServiceDetail(ctx context.Context, in *ValueAddServiceDetailRequest, opts ...grpc_go.CallOption) (*ValueAddServiceDetailResponse, common.ErrorWithAttachment)
@ -75,7 +75,7 @@ type BundleClient interface {
GetVedioWorkDetail(ctx context.Context, in *GetVedioWorkDetailReq, opts ...grpc_go.CallOption) (*GetVedioeWorkDetailResp, common.ErrorWithAttachment)
ToBeComfirmedWorks(ctx context.Context, in *ToBeComfirmedWorksReq, opts ...grpc_go.CallOption) (*ToBeComfirmedWorksResp, common.ErrorWithAttachment)
ConfirmWork(ctx context.Context, in *ConfirmWorkReq, opts ...grpc_go.CallOption) (*ConfirmWorkResp, common.ErrorWithAttachment)
//对账单
// 对账单
GetReconciliationList(ctx context.Context, in *GetReconciliationListReq, opts ...grpc_go.CallOption) (*GetReconciliationListResp, common.ErrorWithAttachment)
CreateReconciliation(ctx context.Context, in *ReconciliationInfo, opts ...grpc_go.CallOption) (*CommonResponse, common.ErrorWithAttachment)
UpdateReconciliation(ctx context.Context, in *ReconciliationInfo, opts ...grpc_go.CallOption) (*CommonResponse, common.ErrorWithAttachment)
@ -473,11 +473,11 @@ type BundleServer interface {
OrderRecordsListV2(context.Context, *OrderRecordsRequestV2) (*OrderRecordsResponseV2, error)
OrderListByOrderNo(context.Context, *OrderInfoByOrderNoRequest) (*OrderInfoByOrderNoResp, error)
OnlyAddValueListByOrderNo(context.Context, *OnlyAddValueListByOrderNoRequest) (*OnlyAddValueListByOrderNoResp, error)
//增值套餐
// 增值套餐
CreateValueAddBundle(context.Context, *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error)
ValueAddBundleList(context.Context, *ValueAddBundleListRequest) (*ValueAddBundleListResponse, error)
ValueAddBundleDetail(context.Context, *ValueAddBundleDetailRequest) (*ValueAddBundleDetailResponse, error)
//新增值服务
// 新增值服务
SaveValueAddService(context.Context, *ValueAddServiceLang) (*SaveResponse, error)
ValueAddServiceList(context.Context, *ValueAddServiceListRequest) (*ValueAddServiceListResponse, error)
ValueAddServiceDetail(context.Context, *ValueAddServiceDetailRequest) (*ValueAddServiceDetailResponse, error)
@ -498,7 +498,7 @@ type BundleServer interface {
GetVedioWorkDetail(context.Context, *GetVedioWorkDetailReq) (*GetVedioeWorkDetailResp, error)
ToBeComfirmedWorks(context.Context, *ToBeComfirmedWorksReq) (*ToBeComfirmedWorksResp, error)
ConfirmWork(context.Context, *ConfirmWorkReq) (*ConfirmWorkResp, error)
//对账单
// 对账单
GetReconciliationList(context.Context, *GetReconciliationListReq) (*GetReconciliationListResp, error)
CreateReconciliation(context.Context, *ReconciliationInfo) (*CommonResponse, error)
UpdateReconciliation(context.Context, *ReconciliationInfo) (*CommonResponse, error)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -43,8 +43,11 @@ type CastClient interface {
RePublish(ctx context.Context, in *RePublishReq, opts ...grpc_go.CallOption) (*RePublishResp, common.ErrorWithAttachment)
DelWork(ctx context.Context, in *DelWorkReq, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment)
WorkInfo(ctx context.Context, in *WorkInfoReq, opts ...grpc_go.CallOption) (*WorkInfoResp, common.ErrorWithAttachment)
OAuthYoutubeUrl(ctx context.Context, in *OAuthYoutubeUrlReq, opts ...grpc_go.CallOption) (*OAuthYoutubeUrlResp, common.ErrorWithAttachment)
OAuthYoutubeToken(ctx context.Context, in *OAuthYoutubeTokenReq, opts ...grpc_go.CallOption) (*OAuthYoutubeTokenResp, common.ErrorWithAttachment)
OAuthAccount(ctx context.Context, in *OAuthAccountReq, opts ...grpc_go.CallOption) (*OAuthAccountResp, common.ErrorWithAttachment)
OAuthCodeToToken(ctx context.Context, in *OAuthCodeToTokenReq, opts ...grpc_go.CallOption) (*OAuthCodeToTokenResp, common.ErrorWithAttachment)
RefreshToken(ctx context.Context, in *RefreshTokenReq, opts ...grpc_go.CallOption) (*RefreshTokenResp, common.ErrorWithAttachment)
PublishMediaInfo(ctx context.Context, in *PublishMediaInfoReq, opts ...grpc_go.CallOption) (*PublishMediaInfoResp, common.ErrorWithAttachment)
Test(ctx context.Context, in *emptypb.Empty, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment)
}
type castClient struct {
@ -66,8 +69,11 @@ type CastClientImpl struct {
RePublish func(ctx context.Context, in *RePublishReq) (*RePublishResp, error)
DelWork func(ctx context.Context, in *DelWorkReq) (*emptypb.Empty, error)
WorkInfo func(ctx context.Context, in *WorkInfoReq) (*WorkInfoResp, error)
OAuthYoutubeUrl func(ctx context.Context, in *OAuthYoutubeUrlReq) (*OAuthYoutubeUrlResp, error)
OAuthYoutubeToken func(ctx context.Context, in *OAuthYoutubeTokenReq) (*OAuthYoutubeTokenResp, error)
OAuthAccount func(ctx context.Context, in *OAuthAccountReq) (*OAuthAccountResp, error)
OAuthCodeToToken func(ctx context.Context, in *OAuthCodeToTokenReq) (*OAuthCodeToTokenResp, error)
RefreshToken func(ctx context.Context, in *RefreshTokenReq) (*RefreshTokenResp, error)
PublishMediaInfo func(ctx context.Context, in *PublishMediaInfoReq) (*PublishMediaInfoResp, error)
Test func(ctx context.Context, in *emptypb.Empty) (*emptypb.Empty, error)
}
func (c *CastClientImpl) GetDubboStub(cc *triple.TripleConn) CastClient {
@ -166,16 +172,34 @@ func (c *castClient) WorkInfo(ctx context.Context, in *WorkInfoReq, opts ...grpc
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/WorkInfo", in, out)
}
func (c *castClient) OAuthYoutubeUrl(ctx context.Context, in *OAuthYoutubeUrlReq, opts ...grpc_go.CallOption) (*OAuthYoutubeUrlResp, common.ErrorWithAttachment) {
out := new(OAuthYoutubeUrlResp)
func (c *castClient) OAuthAccount(ctx context.Context, in *OAuthAccountReq, opts ...grpc_go.CallOption) (*OAuthAccountResp, common.ErrorWithAttachment) {
out := new(OAuthAccountResp)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/OAuthYoutubeUrl", in, out)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/OAuthAccount", in, out)
}
func (c *castClient) OAuthYoutubeToken(ctx context.Context, in *OAuthYoutubeTokenReq, opts ...grpc_go.CallOption) (*OAuthYoutubeTokenResp, common.ErrorWithAttachment) {
out := new(OAuthYoutubeTokenResp)
func (c *castClient) OAuthCodeToToken(ctx context.Context, in *OAuthCodeToTokenReq, opts ...grpc_go.CallOption) (*OAuthCodeToTokenResp, common.ErrorWithAttachment) {
out := new(OAuthCodeToTokenResp)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/OAuthYoutubeToken", in, out)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/OAuthCodeToToken", in, out)
}
func (c *castClient) RefreshToken(ctx context.Context, in *RefreshTokenReq, opts ...grpc_go.CallOption) (*RefreshTokenResp, common.ErrorWithAttachment) {
out := new(RefreshTokenResp)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/RefreshToken", in, out)
}
func (c *castClient) PublishMediaInfo(ctx context.Context, in *PublishMediaInfoReq, opts ...grpc_go.CallOption) (*PublishMediaInfoResp, common.ErrorWithAttachment) {
out := new(PublishMediaInfoResp)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/PublishMediaInfo", in, out)
}
func (c *castClient) Test(ctx context.Context, in *emptypb.Empty, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment) {
out := new(emptypb.Empty)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/Test", in, out)
}
// CastServer is the server API for Cast service.
@ -196,8 +220,11 @@ type CastServer interface {
RePublish(context.Context, *RePublishReq) (*RePublishResp, error)
DelWork(context.Context, *DelWorkReq) (*emptypb.Empty, error)
WorkInfo(context.Context, *WorkInfoReq) (*WorkInfoResp, error)
OAuthYoutubeUrl(context.Context, *OAuthYoutubeUrlReq) (*OAuthYoutubeUrlResp, error)
OAuthYoutubeToken(context.Context, *OAuthYoutubeTokenReq) (*OAuthYoutubeTokenResp, error)
OAuthAccount(context.Context, *OAuthAccountReq) (*OAuthAccountResp, error)
OAuthCodeToToken(context.Context, *OAuthCodeToTokenReq) (*OAuthCodeToTokenResp, error)
RefreshToken(context.Context, *RefreshTokenReq) (*RefreshTokenResp, error)
PublishMediaInfo(context.Context, *PublishMediaInfoReq) (*PublishMediaInfoResp, error)
Test(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
mustEmbedUnimplementedCastServer()
}
@ -248,11 +275,20 @@ func (UnimplementedCastServer) DelWork(context.Context, *DelWorkReq) (*emptypb.E
func (UnimplementedCastServer) WorkInfo(context.Context, *WorkInfoReq) (*WorkInfoResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method WorkInfo not implemented")
}
func (UnimplementedCastServer) OAuthYoutubeUrl(context.Context, *OAuthYoutubeUrlReq) (*OAuthYoutubeUrlResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method OAuthYoutubeUrl not implemented")
func (UnimplementedCastServer) OAuthAccount(context.Context, *OAuthAccountReq) (*OAuthAccountResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method OAuthAccount not implemented")
}
func (UnimplementedCastServer) OAuthYoutubeToken(context.Context, *OAuthYoutubeTokenReq) (*OAuthYoutubeTokenResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method OAuthYoutubeToken not implemented")
func (UnimplementedCastServer) OAuthCodeToToken(context.Context, *OAuthCodeToTokenReq) (*OAuthCodeToTokenResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method OAuthCodeToToken not implemented")
}
func (UnimplementedCastServer) RefreshToken(context.Context, *RefreshTokenReq) (*RefreshTokenResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method RefreshToken not implemented")
}
func (UnimplementedCastServer) PublishMediaInfo(context.Context, *PublishMediaInfoReq) (*PublishMediaInfoResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method PublishMediaInfo not implemented")
}
func (UnimplementedCastServer) Test(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method Test not implemented")
}
func (s *UnimplementedCastServer) XXX_SetProxyImpl(impl protocol.Invoker) {
s.proxyImpl = impl
@ -688,8 +724,8 @@ func _Cast_WorkInfo_Handler(srv interface{}, ctx context.Context, dec func(inter
return interceptor(ctx, in, info, handler)
}
func _Cast_OAuthYoutubeUrl_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(OAuthYoutubeUrlReq)
func _Cast_OAuthAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(OAuthAccountReq)
if err := dec(in); err != nil {
return nil, err
}
@ -701,7 +737,7 @@ func _Cast_OAuthYoutubeUrl_Handler(srv interface{}, ctx context.Context, dec fun
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("OAuthYoutubeUrl", args, invAttachment)
invo := invocation.NewRPCInvocation("OAuthAccount", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
@ -717,8 +753,8 @@ func _Cast_OAuthYoutubeUrl_Handler(srv interface{}, ctx context.Context, dec fun
return interceptor(ctx, in, info, handler)
}
func _Cast_OAuthYoutubeToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(OAuthYoutubeTokenReq)
func _Cast_OAuthCodeToToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(OAuthCodeToTokenReq)
if err := dec(in); err != nil {
return nil, err
}
@ -730,7 +766,94 @@ func _Cast_OAuthYoutubeToken_Handler(srv interface{}, ctx context.Context, dec f
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("OAuthYoutubeToken", args, invAttachment)
invo := invocation.NewRPCInvocation("OAuthCodeToToken", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Cast_RefreshToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(RefreshTokenReq)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("RefreshToken", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Cast_PublishMediaInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(PublishMediaInfoReq)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("PublishMediaInfo", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Cast_Test_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("Test", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
@ -810,12 +933,24 @@ var Cast_ServiceDesc = grpc_go.ServiceDesc{
Handler: _Cast_WorkInfo_Handler,
},
{
MethodName: "OAuthYoutubeUrl",
Handler: _Cast_OAuthYoutubeUrl_Handler,
MethodName: "OAuthAccount",
Handler: _Cast_OAuthAccount_Handler,
},
{
MethodName: "OAuthYoutubeToken",
Handler: _Cast_OAuthYoutubeToken_Handler,
MethodName: "OAuthCodeToToken",
Handler: _Cast_OAuthCodeToToken_Handler,
},
{
MethodName: "RefreshToken",
Handler: _Cast_RefreshToken_Handler,
},
{
MethodName: "PublishMediaInfo",
Handler: _Cast_PublishMediaInfo_Handler,
},
{
MethodName: "Test",
Handler: _Cast_Test_Handler,
},
},
Streams: []grpc_go.StreamDesc{},

View File

@ -21,6 +21,7 @@ import (
"fmt"
"fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/cache"
"fonchain-fiee/pkg/common"
"fonchain-fiee/pkg/logger"
"fonchain-fiee/pkg/router"
)
@ -55,6 +56,7 @@ func bootstrap() (err error) {
}
cache.LoadRedis(redisConfig)
common.Init()
//
//gpt.InitSet(configEnv.Ai.Host, configEnv.Ai.TelNum, configEnv.Ai.Password)
return nil

View File

@ -4,10 +4,11 @@ import (
"errors"
"fmt"
"fonchain-fiee/pkg/common"
"github.com/BurntSushi/toml"
"gopkg.in/ini.v1"
"os"
"strconv"
"github.com/BurntSushi/toml"
"gopkg.in/ini.v1"
)
var (
@ -108,15 +109,16 @@ type Redis struct {
}
type System struct {
Mode string
AppMode string
Version string
HttpPort string
Host string
RedirectUri string
Domain string
ErpHost string
FieeHost string
Mode string
AppMode string
Version string
HttpPort string
Host string
RedirectUri string
Domain string
ErpHost string
FieeHost string
AuthRedirectUrl string
}
type Oss struct {
AccessKeyId string

15
data/policy.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>隐私政策</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; }
</style>
</head>
<body>
<p>H5系统隐私政策</p><p>生效日期2025年9月2日</p><p>1. 信息收集与使用</p><p>收集的个人信息类型</p><p>基础信息手机号、微信昵称、头像、设备信息IMEI、操作系统版本</p><p>支付信息:订单号、支付金额、交易时间(通过加密方式传输,不存储银行卡信息)。</p><p>自媒体平台授权信息,例如:</p><p>抖音API接口权限仅限内容同步</p><p>内容生成数据:用户输入的关键词、风格偏好、历史生成内容。</p><p>收集目的</p><p>账号验证与支付:手机号用于身份核验,支付信息用于完成交易。</p><p>内容发布通过授权信息调用第三方平台API执行用户指令。</p><p>2. 信息共享与转让</p><p>第三方平台接口仅在用户授权范围内调用API不共享用户平台账号密码。</p><p>支付合作方:与微信支付、支付宝等服务商共享订单信息以完成交易。</p><p>法律要求:如配合司法机关调查或响应政府要求,可能披露必要信息。</p><p>3. 用户权利</p><p>访问与控制:</p><p>可随时在“个人中心”查看历史生成内容、订单记录及授权状态。</p><p>删除与撤回:</p><p>可申请删除个人账户及生成内容(不包含已同步至第三方平台的内容)。</p><p>4. 数据安全</p><p>技术措施采用HTTPS加密传输、数据库脱敏存储定期进行安全漏洞检测。</p><p>第三方SDK仅接入必要SDK如微信登录、地图服务等并签署数据保密协议。</p><p>5. 隐私政策更新</p><p>如涉及重大变更(如新增数据收集项),将通过短信/邮件通知并要求重新授权。</p><p>6. 联系我们</p><p>如有疑问,请通过以下方式联系:</p><p>客服邮箱develop@fiee.com</p>
</body>
</html>

15
data/service.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>服务条款</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; }
</style>
</head>
<body>
<p>服务条款</p><p>生效日期2025年9月2日</p><p>1. 接受条款</p><p>通过注册或使用本系统(以下简称“服务”),您确认已阅读、理解并同意以下条款。若您不同意本条款,请立即停止使用本服务。</p><p>2. 服务内容</p><p>本系统提供自媒体内容生成与发布服务,用户可购买套餐后生成图文、短视频等原创内容,并通过系统接口同步至指定自媒体平台(如微信公众号、抖音、小红书等)。</p><p>服务包含AI辅助创作、内容优化建议、一键发布功能具体内容以实际功能模块为准。</p><p>3. 用户权利与义务</p><p>账号与支付</p><p>注册需提供手机号或微信授权,购买套餐需通过系统支持的支付方式(如微信支付、支付宝)。</p><p>您需确保支付账户信息真实有效,因虚假信息导致的损失由您自行承担。</p><p>内容生成与发布</p><p>生成内容为原创作品,最终发布权归用户所有。</p><p>用户需对生成内容进行人工审核,确认无误后方可发布。因内容违规导致的平台处罚或法律纠纷,责任由用户承担。</p><p>系统不对用户未审核内容的合规性负责,但有权对明显违法内容(如涉黄、涉政)进行过滤拦截。</p><p>第三方平台接口</p><p>用户需授权系统调用其自媒体平台API如tiktok开发者接口授权范围限于内容发布及数据同步。</p><p>授权信息仅用于执行用户指令,系统不存储用户平台账号密码等敏感信息。</p><p>4. 套餐与退款</p><p>套餐有效期自购买成功日起计算,逾期未使用的套餐不支持退款。</p><p>如因系统故障或服务瑕疵导致无法正常使用,可联系客服协商退款。</p><p>5. 知识产权</p><p>由系统生成的内容(如文案、图片)默认无版权归属限制,但用户需遵守以下规则:</p><p>不得将内容用于商业用途(如转售、批量分发);</p><p>不得篡改系统生成内容的署名或标识;</p><p>若内容包含第三方素材(如音乐、图片),用户需自行确保使用合法性。</p><p>6. 责任限制</p><p>系统不对以下情形承担责任:</p><p>用户未审核内容导致的平台封禁、投诉或法律风险;</p><p>第三方平台接口变更或服务中断;</p><p>因不可抗力(如网络故障)导致的发布延迟。</p><p>7. 其他</p><p>本条款解释权归服务提供方所有,更新后将在系统内公示,用户继续使用即视为同意。</p>
</body>
</html>

View File

@ -6,6 +6,8 @@ Host = "https://common.szjixun.cn"
RedirectUri = "/api/redirect/url"
ErpHost = "http://114.218.158.24:9020"
FieeHost = "http://114.218.158.24:9020"
AuthRedirectUrl = "http://172.16.100.99:9028/media_account"
[bos]
Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe"
Sk = "d2ecaa9d75114d3b9f42b99014198306"

View File

@ -6,6 +6,7 @@ Host = "https://common.szjixun.cn"
RedirectUri = "/api/redirect/url"
ErpHost = "https://erpapi.fontree.cn"
FieeHost = "https://erpapi.fiee.com"
AuthRedirectUrl = "https://erp.fiee.com/media_account"
[bos]
Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe"
Sk = "d2ecaa9d75114d3b9f42b99014198306"

View File

@ -6,6 +6,8 @@ Host = "https://common.szjixun.cn"
RedirectUri = "/api/redirect/url"
ErpHost = "http://114.218.158.24:9020"
FieeHost = "http://114.218.158.24:9020"
FieeApiHost = "https://saas-test.szjixun.cn"
AuthRedirectUrl = "http://172.16.100.99:9028/media_account"
[bos]
Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe"
Sk = "d2ecaa9d75114d3b9f42b99014198306"

11
pkg/common/file.go Normal file
View File

@ -0,0 +1,11 @@
package common
import "fonchain-fiee/pkg/utils"
func Init() {
_ = utils.CreateDirPath("./runtime")
utils.CopyFile("./data/policy.html", "./runtime")
utils.CopyFile("./data/service.html", "./runtime")
utils.CopyFile("../data/policy.html", "./runtime")
utils.CopyFile("../data/service.html", "./runtime")
}

View File

@ -4,11 +4,12 @@ import (
"fonchain-fiee/pkg/middleware"
"fonchain-fiee/pkg/service"
serviceCast "fonchain-fiee/pkg/service/cast"
"github.com/gin-gonic/gin"
)
func MediaRouter(r *gin.RouterGroup) {
//noAuth := r.Group("")
noAuth := r.Group("")
auth := r.Group("")
auth.Use(middleware.CheckWebLogin(service.AccountProvider))
media := auth.Group("media")
@ -17,12 +18,14 @@ func MediaRouter(r *gin.RouterGroup) {
media.POST("unbind-manager", serviceCast.UnbindManager)
media.POST("bind-manager", serviceCast.BindManager)
media.POST("update-account", serviceCast.UpdateMediaAccount)
media.POST("oauth-url", serviceCast.OAuthUrl)
media.POST("oauth-account", serviceCast.OAuthAccount)
media.POST("refresh-token", serviceCast.RefreshToken)
}
mediaNoLogin := r.Group("media")
{
mediaNoLogin.GET("oauth2callback", serviceCast.OAuth2Callback)
mediaNoLogin.GET("test", serviceCast.Test)
mediaNoLogin.Any("test", serviceCast.Test)
//mediaNoLogin.GET("dmoauth2callback", serviceCast.DMOAuth2Callback)
}
work := auth.Group("work")
@ -35,5 +38,11 @@ func MediaRouter(r *gin.RouterGroup) {
work.POST("republish", serviceCast.RePublish)
work.POST("delete", serviceCast.DelWork)
work.POST("remind", serviceCast.Remind)
work.POST("publish-info", serviceCast.PublishInfo)
}
social := noAuth.Group("social")
{
social.GET("tiktok-redirect", serviceCast.TikTokRedirect)
}
}

View File

@ -192,6 +192,8 @@ func NewRouter() *gin.Engine {
importRoute.Use(middleware.CheckWebLogin(service.AccountProvider))
importRoute.POST("data/bind", imports.ImportBind)
importRoute.POST("data/publish", imports.ImportPublish)
importRoute.POST("data/publish2", imports.ImportPublishV2)
importRoute.POST("data/publish3", imports.ImportPublishV3)
importRoute.POST("data/confirm", imports.WorkConfirm)
}
//静态文件

View File

@ -24,6 +24,33 @@ func GetReconciliationList(c *gin.Context) {
service.Error(c, err)
return
}
res := &bundle.GetReconciliationListResp{}
// Step 1: 如果有姓名/电话筛选,先查用户列表
if req.UserName != "" {
userListResp, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
BlurNameTel: req.UserName,
Domain: "app",
})
if err != nil {
service.Error(c, err)
return
}
if len(userListResp.UserList) == 0 {
// 没查到用户,直接返回空结果
res.Page = req.Page
res.PageSize = req.PageSize
res.Total = 0
service.Success(c, res)
return
}
// 提取用户ID列表
for _, u := range userListResp.UserList {
req.UserIDS = append(req.UserIDS, u.Id)
}
}
req.UserName = ""
detail, detailErr := service.BundleProvider.GetReconciliationList(context.Background(), &req)
if detailErr != nil {
service.Error(c, detailErr)
@ -63,6 +90,31 @@ func GetReconciliationListDownload(c *gin.Context) {
service.Error(c, err)
return
}
res := &bundle.GetReconciliationListResp{}
if req.UserName != "" {
userListResp, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
BlurNameTel: req.UserName,
Domain: "app",
})
if err != nil {
service.Error(c, err)
return
}
if len(userListResp.UserList) == 0 {
// 没查到用户,直接返回空结果
res.Page = req.Page
res.PageSize = req.PageSize
res.Total = 0
service.Success(c, res)
return
}
// 提取用户ID列表
for _, u := range userListResp.UserList {
req.UserIDS = append(req.UserIDS, u.Id)
}
}
detail, detailErr := service.BundleProvider.GetReconciliationList(context.Background(), &req)
if detailErr != nil {
service.Error(c, detailErr)
@ -92,7 +144,7 @@ func GetReconciliationListDownload(c *gin.Context) {
}
}
titleList := []string{
"关联套餐订单号", "关联增值服务订单号", "对账单创建时间", "用户编号", "艺人", "艺人手机号", "套餐", "支付金额", "手续费", "币种", "支付渠道", "支付时间", "支付状态", "流水号",
"关联套餐订单号", "关联增值服务订单号", "用户编号", "艺人", "艺人手机号", "套餐", "支付金额", "手续费", "币种", "支付渠道", "支付时间", "支付状态", "流水号",
}
var dataList []interface{}
@ -106,7 +158,7 @@ func GetReconciliationListDownload(c *gin.Context) {
data := []any{
i.BundleOrderOn,
i.BundleAddOrderOn,
i.CreationTime,
//i.CreationTime,
i.SubNum,
i.UserName,
i.UserTel,
@ -306,7 +358,7 @@ func AutoCreateUserAndOrder(c *gin.Context) {
)
// 当前 未将 签名 写入合同中 todo 金额和有效时间待修改
contract := "https://e-cdn.fontree.cn/fonchain-main/prod/file/saas/contract/template-25032801.pdf"
expirationDay = t.AddDate(10, 0, 0).Format("2006-01-02")
expirationDay = t.AddDate(1, 0, 0).Format("2006-01-02")
signContract, signContractErr := logic.SignContractV2(userReq.UserNum, contract, TotalPrice, expirationDay)
if signContractErr != nil {
service.Error(c, signContractErr)

View File

@ -3,6 +3,7 @@ package cast
import (
"context"
"errors"
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/bundle"
"fonchain-fiee/api/cast"
@ -10,9 +11,13 @@ import (
"fonchain-fiee/pkg/e"
modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/service"
"net/http"
"net/url"
"strconv"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"strconv"
"google.golang.org/protobuf/types/known/emptypb"
)
func MediaUserList(ctx *gin.Context) {
@ -160,9 +165,10 @@ func UpdateMediaAccount(ctx *gin.Context) {
return
}
func OAuthUrl(ctx *gin.Context) {
var req *cast.OAuthYoutubeUrlReq
var resp *cast.OAuthYoutubeUrlResp
// 账号授权
func OAuthAccount(ctx *gin.Context) {
var req *cast.OAuthAccountReq
var resp *cast.OAuthAccountResp
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
@ -172,7 +178,7 @@ func OAuthUrl(ctx *gin.Context) {
service.Error(ctx, err)
return
}
if resp, err = service.CastProvider.OAuthYoutubeUrl(ctx, req); err != nil {
if resp, err = service.CastProvider.OAuthAccount(ctx, req); err != nil {
service.Error(ctx, err)
return
}
@ -181,15 +187,63 @@ func OAuthUrl(ctx *gin.Context) {
}
func OAuth2Callback(ctx *gin.Context) {
var (
platformIds string
userID string
)
code := ctx.Query("code")
state := ctx.Query("state")
//scope := ctx.Query("scope")
resp, err := service.CastProvider.OAuthYoutubeToken(ctx, &cast.OAuthYoutubeTokenReq{
MediaAccountUuid: state,
Code: code,
//scope: scope,
})
stateMM, _ := url.ParseQuery(state)
if len(stateMM["platform_id"]) > 0 {
platformIds = stateMM["platform_id"][0]
}
if len(stateMM["user_id"]) > 0 {
userID = stateMM["user_id"][0]
}
platformID, _ := strconv.ParseInt(platformIds, 10, 64)
req := &cast.OAuthCodeToTokenReq{
Code: code,
UserID: userID,
}
switch platformID {
case int64(cast.PlatformIDENUM_DM):
req.PlatformID = cast.PlatformIDENUM_DM
case int64(cast.PlatformIDENUM_TIKTOK):
req.PlatformID = cast.PlatformIDENUM_TIKTOK
default:
service.Error(ctx, errors.New(e.GetMsg(e.InvalidParams)))
return
}
zap.L().Info("OAuth2Callback", zap.Any("req", req), zap.Any("code", code), zap.Any("state", state))
_, err := service.CastProvider.OAuthCodeToToken(ctx, req)
if err != nil {
zap.L().Info("OAuth2Callback error", zap.Error(err))
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?%s", config.AppConfig.System.AuthRedirectUrl, "status=1"))
return
}
// TODO 跳转到前端页面
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?%s", config.AppConfig.System.AuthRedirectUrl, "status=0"))
//service.Success(ctx, map[string]interface{}{
// "req": req,
// "resp": resp,
//})
return
}
func RefreshToken(ctx *gin.Context) {
var req *cast.RefreshTokenReq
var resp *cast.RefreshTokenResp
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
if resp, err = service.CastProvider.RefreshToken(ctx, req); err != nil {
service.Error(ctx, err)
return
}
@ -198,6 +252,6 @@ func OAuth2Callback(ctx *gin.Context) {
}
func Test(ctx *gin.Context) {
service.Success(ctx, nil)
service.CastProvider.Test(ctx, &emptypb.Empty{})
return
}

View File

@ -1 +0,0 @@
package cast

View File

@ -0,0 +1,38 @@
package cast
import (
"fmt"
"fonchain-fiee/pkg/service"
"net/url"
"github.com/gin-gonic/gin"
)
func TikTokRedirect(ctx *gin.Context) {
var state, decodeParams, code string
var err error
var values url.Values
fmt.Println(code)
state = ctx.Query("state")
decodeParams, err = url.QueryUnescape(state)
values, err = url.ParseQuery(decodeParams)
if err != nil {
service.Error(ctx, err)
return
}
switch values.Get("source") {
case "authcode":
code = ctx.Query("code")
/*resp, _err := service.CastProvider.OAuthTikTokToken(ctx, &cast.OAuthTikTokTokenReq{
Code: code,
UserID: values.Get("userid"),
})
if _err != nil {
service.Error(ctx, _err)
return
}
service.Success(ctx, resp)*/
}
return
}

View File

@ -11,7 +11,9 @@ import (
"fonchain-fiee/pkg/e"
modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils/stime"
"strconv"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
@ -31,6 +33,7 @@ func UpdateWorkImage(ctx *gin.Context) {
ID: artistId,
Domain: "app",
})
zap.L().Info("UpdateWorkImage infoResp", zap.Any("infoResp", infoResp))
if err != nil {
service.Error(ctx, err)
return
@ -53,6 +56,7 @@ func UpdateWorkImage(ctx *gin.Context) {
newCtx := NewCtxWithUserInfo(ctx)
req.Source = 1
resp, err := service.CastProvider.UpdateWorkImage(newCtx, req)
zap.L().Info("UpdateWorkImage resp", zap.Any("resp", resp))
if err != nil {
service.Error(ctx, err)
return
@ -75,6 +79,7 @@ func UpdateWorkVideo(ctx *gin.Context) {
ID: artistId,
Domain: "app",
})
zap.L().Info("UpdateWorkVideo", zap.Any("infoResp", infoResp))
if err != nil {
service.Error(ctx, err)
return
@ -97,6 +102,7 @@ func UpdateWorkVideo(ctx *gin.Context) {
newCtx := NewCtxWithUserInfo(ctx)
req.Source = 1
resp, err := service.CastProvider.UpdateWorkVideo(newCtx, req)
zap.L().Info("UpdateWorkVideo", zap.Any("resp", resp))
if err != nil {
service.Error(ctx, err)
return
@ -146,6 +152,14 @@ func WorkList(ctx *gin.Context) {
approvalID, _ := strconv.ParseUint(v.ApprovalID, 10, 64)
workUuidApprovalIDMap[int(approvalID)] = v.WorkUuid
}
statusUpdateDate, _ := stime.DatetimeToTimes(v.StatusUpdateTime, "2006-01-02 15:04:05")
if v.WorkStatus == 6 && (time.Now().Unix()-int64(statusUpdateDate) < 300) {
go func() {
_, _ = service.CastProvider.PublishMediaInfo(context.Background(), &cast.PublishMediaInfoReq{
WorkUuid: v.WorkUuid,
})
}()
}
}
if len(workUuidApprovalIDMap) > 0 {
_ = RefreshWorkApprovalStatus(ctx, workUuidApprovalIDMap)
@ -176,8 +190,10 @@ func WorkDetail(ctx *gin.Context) {
}
func CheckUserBundleBalance(userID int32, balanceType modelCast.BalanceTypeEnum) (err error) {
zap.L().Info("CheckUserBundleBalance", zap.Any("userID", userID))
resp, err := service.BundleProvider.GetBundleBalanceByUserId(context.Background(), &bundle.GetBundleBalanceByUserIdReq{UserId: userID})
if err != nil {
zap.L().Error("CheckUserBundleBalance", zap.Any("err", err))
return
}
zap.L().Info("CheckUserBundleBalance", zap.Any("resp", resp))
@ -206,6 +222,7 @@ func RePublish(ctx *gin.Context) {
var (
req *cast.RePublishReq
resp *cast.RePublishResp
//workInfoResp *cast.WorkInfoResp
)
var err error
if err = ctx.ShouldBind(&req); err != nil {
@ -213,28 +230,39 @@ func RePublish(ctx *gin.Context) {
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err = service.CastProvider.RePublish(newCtx, req)
if err != nil {
/* workInfoResp, err = service.CastProvider.WorkInfo(context.Background(), &cast.WorkInfoReq{
WorkUuid: req.WorkUuid,
})
if err != nil {
service.Error(ctx, err)
return
}
fmt.Println(workInfoResp)*/
/*artistID, _ := strconv.ParseInt(workInfoResp.ArtistUuid, 10, 64)
if err = CheckUserBundleBalance(int32(artistID), modelCast.BalanceTypeVideoValue); err != nil {
service.Error(ctx, err)
return
}
artistID, _ := strconv.ParseUint(resp.ArtistUuid, 10, 64)
balanceReq := &bundle.AddBundleBalanceReq{
UserId: int32(artistID),
}
if resp.WorkCategory == 1 {
if workInfoResp.WorkCategory == 1 {
balanceReq.ImageConsumptionNumber = 1
}
if resp.WorkCategory == 2 {
if workInfoResp.WorkCategory == 2 {
balanceReq.VideoConsumptionNumber = 1
}
_, err = service.BundleProvider.AddBundleBalance(context.Background(), balanceReq)
if err != nil {
service.Error(ctx, err)
//FIXME 进行回滚
return
}*/
resp, err = service.CastProvider.RePublish(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
service.Success(ctx, resp)
return
}
@ -364,3 +392,20 @@ func Remind(ctx *gin.Context) {
service.Success(ctx, nil)
return
}
func PublishInfo(ctx *gin.Context) {
var req *cast.PublishMediaInfoReq
var resp *cast.PublishMediaInfoResp
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
resp, err = service.CastProvider.PublishMediaInfo(context.Background(), req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}

View File

@ -4,13 +4,20 @@ import (
apiCast "fonchain-fiee/api/cast"
)
type AccountInfo struct {
NickName string `json:"nickName"`
AccountId string `json:"accountId"`
}
type ArtistAccount struct {
Name string `json:"name"`
Account map[apiCast.PlatformIDENUM]string `json:"account"`
Index string `json:"index"`
Name string `json:"name"`
SubNum string `json:"subNum"`
Account map[apiCast.PlatformIDENUM]AccountInfo `json:"account"`
}
type ArtistMedia struct {
Id string `json:"id"`
Name string `json:"name"`
SubNum string `json:"subNum"`
Title string `json:"title"`
Img string `json:"img"`
Video string `json:"video"`
@ -31,6 +38,8 @@ type ArtistVideoDetail struct {
Youtube string `json:"youtube"`
Instagram string `json:"instagram"`
TikTok string `json:"tiktok"`
ArtistId string `json:"artistId"`
SubNum string `json:"subNum"`
}
type CreateRequest struct {
Type string `json:"Type"`

View File

@ -13,6 +13,7 @@ import (
modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/cast"
"fonchain-fiee/pkg/service/upload"
"log"
"os"
"path/filepath"
@ -23,11 +24,9 @@ import (
"github.com/xuri/excelize/v2"
)
type artu struct {
}
func ImportBind(c *gin.Context) {
var failedRecords []FailedRecord
var record []*apiCast.UpdateMediaAccountReq
// 1. 上传文件
excelFile, err := c.FormFile("excel")
if err != nil {
@ -58,7 +57,8 @@ func ImportBind(c *gin.Context) {
}
}
res, err := service.AccountFieeProvider.UserList(context.Background(), &account.UserListRequest{
Name: artist.Name,
Name: artist.Name,
SubNum: artist.SubNum,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
@ -68,6 +68,14 @@ func ImportBind(c *gin.Context) {
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
continue
}
if res.Count == 0 {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: "未找到用户信息",
})
log.Printf("未找到用户信息")
continue
}
if res != nil && len(res.UserList) > 0 {
var infoResp *account.UserInfoResponse
var err error
@ -119,11 +127,11 @@ func ImportBind(c *gin.Context) {
log.Printf(fmt.Sprintf("增加账户数量失败: %s", err.Error()))
continue
}
_, err = service.CastProvider.UpdateMediaAccount(c, &apiCast.UpdateMediaAccountReq{
mediaAccountInfoRes, err := service.CastProvider.UpdateMediaAccount(c, &apiCast.UpdateMediaAccountReq{
ArtistUuid: strconv.FormatUint(res.UserList[0].Id, 10),
PlatformID: accountType,
PlatformUserName: artist.Name,
PlatformUserID: accountInfo,
PlatformUserName: accountInfo.NickName,
PlatformUserID: accountInfo.AccountId,
ArtistName: infoResp.Name,
ArtistPhone: infoResp.TelNum,
ArtistPhoneAreaCode: infoResp.TelAreaCode,
@ -134,23 +142,39 @@ func ImportBind(c *gin.Context) {
Msg: fmt.Sprintf("绑定账户信息失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("绑定账户信息失败: %s", err.Error()))
_, err = service.BundleProvider.AddBundleBalance(context.Background(), &bundle.AddBundleBalanceReq{
_, err1 := service.BundleProvider.AddBundleBalance(context.Background(), &bundle.AddBundleBalanceReq{
UserId: int32(res.UserList[0].Id),
AccountConsumptionNumber: -1,
})
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("绑定失败后减少余额失败: %s", err.Error()),
})
log.Println(fmt.Sprintf("绑定失败后减少余额失败: %s", err.Error()), errors.New(e.GetMsg(e.InvalidParams)))
if err1 != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("绑定失败后减少余额失败: %s", err1.Error()),
})
log.Println(fmt.Sprintf("绑定失败后减少余额失败: %s", err1.Error()), errors.New(e.GetMsg(e.InvalidParams)))
}
continue
}
record = append(record, &apiCast.UpdateMediaAccountReq{
MediaAccountUuid: mediaAccountInfoRes.MediaAccountUuid,
ArtistUuid: strconv.FormatUint(res.UserList[0].Id, 10),
PlatformID: accountType,
PlatformUserName: accountInfo.NickName,
PlatformUserID: accountInfo.AccountId,
ArtistName: infoResp.Name,
ArtistPhone: infoResp.TelNum,
ArtistPhoneAreaCode: infoResp.TelAreaCode,
})
}
}
}
excelUrl, err := exportBindingRecordsToExcel(record)
// 5. 返回结果
service.Success(c, failedRecords)
service.Success(c, map[string]interface{}{
"excelUrl": excelUrl,
"failedRecords": failedRecords,
"accountInfo": artists,
})
}
func readArtistAccountInfo(excelPath string) ([]ArtistAccount, error) {
@ -171,29 +195,96 @@ func readArtistAccountInfo(excelPath string) ([]ArtistAccount, error) {
if i == 0 || len(row) < 2 {
continue
}
if i == 58 {
break
}
tmp := ArtistAccount{
Account: make(map[apiCast.PlatformIDENUM]string),
Account: make(map[apiCast.PlatformIDENUM]AccountInfo),
Index: strings.TrimSpace(row[0]),
Name: strings.TrimSpace(row[1]),
SubNum: strings.TrimSpace(row[2]),
}
youtube, _ := f.GetCellValue(sheetName, fmt.Sprintf("C%d", i+1))
if youtube != "" {
tmp.Account[2] = strings.TrimSpace(youtube)
youtubeId, _ := f.GetCellValue(sheetName, fmt.Sprintf("D%d", i+1))
youtubeNickName, _ := f.GetCellValue(sheetName, fmt.Sprintf("E%d", i+1))
youtube := AccountInfo{
AccountId: strings.TrimSpace(youtubeId),
NickName: strings.TrimSpace(youtubeNickName),
}
tmp.Account[2] = youtube
insId, _ := f.GetCellValue(sheetName, fmt.Sprintf("F%d", i+1))
insNickName, _ := f.GetCellValue(sheetName, fmt.Sprintf("G%d", i+1))
ins := AccountInfo{
AccountId: strings.TrimSpace(insId),
NickName: strings.TrimSpace(insNickName),
}
ins, _ := f.GetCellValue(sheetName, fmt.Sprintf("D%d", i+1))
if ins != "" {
tmp.Account[3] = strings.TrimSpace(ins)
tmp.Account[3] = ins
tiktokId, _ := f.GetCellValue(sheetName, fmt.Sprintf("H%d", i+1))
tiktokNiackName, _ := f.GetCellValue(sheetName, fmt.Sprintf("I%d", i+1))
tiktok := AccountInfo{
AccountId: strings.TrimSpace(tiktokId),
NickName: strings.TrimSpace(tiktokNiackName),
}
tiktok, _ := f.GetCellValue(sheetName, fmt.Sprintf("E%d", i+1))
if tiktok != "" {
tmp.Account[1] = strings.TrimSpace(tiktok)
tmp.Account[1] = tiktok
}
artists = append(artists, tmp)
}
return artists, nil
}
func exportBindingRecordsToExcel(records []*apiCast.UpdateMediaAccountReq) (string, error) {
fileDir := "./runtime/import/"
filename := "绑定账户记录.xlsx"
filePath := filepath.Join(fileDir, filename)
// 创建目录
_ = os.MkdirAll(fileDir, os.ModePerm)
var f *excelize.File
sheet := "Sheet1"
// 判断文件是否存在
if _, err := os.Stat(filePath); os.IsNotExist(err) {
f = excelize.NewFile()
f.NewSheet(sheet)
headers := []string{"序号", "MediaAccountUuid", "ArtistUuid", "PlatformID", "PlatformUserName", "PlatformUserID", "ArtistName", "ArtistPhone", "ArtistPhoneAreaCode"}
for col, h := range headers {
_ = f.SetCellValue(sheet, fmt.Sprintf("%c1", 'A'+col), h)
}
} else {
var err error
f, err = excelize.OpenFile(filePath)
if err != nil {
return "", err
}
}
// 找到最后一行
rows, err := f.GetRows(sheet)
if err != nil {
return "", err
}
startRow := len(rows) + 1
// 写入数据
for i, r := range records {
row := startRow + i
_ = f.SetCellValue(sheet, fmt.Sprintf("A%d", row), i+1)
_ = f.SetCellValue(sheet, fmt.Sprintf("B%d", row), r.MediaAccountUuid)
_ = f.SetCellValue(sheet, fmt.Sprintf("C%d", row), r.ArtistUuid)
_ = f.SetCellValue(sheet, fmt.Sprintf("D%d", row), r.PlatformID)
_ = f.SetCellValue(sheet, fmt.Sprintf("E%d", row), r.PlatformUserName)
_ = f.SetCellValue(sheet, fmt.Sprintf("F%d", row), r.PlatformUserID)
_ = f.SetCellValue(sheet, fmt.Sprintf("G%d", row), r.ArtistName)
_ = f.SetCellValue(sheet, fmt.Sprintf("H%d", row), r.ArtistPhone)
_ = f.SetCellValue(sheet, fmt.Sprintf("I%d", row), r.ArtistPhoneAreaCode)
}
// 保存
if err = f.SaveAs(filePath); err != nil {
return "", err
}
// 上传
excelUrl, err := upload.PutBos(filePath, "excel", true)
if err != nil {
return "", err
}
return excelUrl, nil
}

View File

@ -1,52 +1,53 @@
package imports
import (
"context"
"errors"
"fmt"
account "fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/bundle"
apiCast "fonchain-fiee/api/cast"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/cast"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/xuri/excelize/v2"
)
func WorkConfirm(c *gin.Context) { // 确认作品并扣除余量
newCtx := cast.NewCtxWithUserInfo(c)
list, err := service.CastProvider.WorkList(newCtx, &apiCast.WorkListReq{
WorkStatus: 4,
Page: 1,
PageSize: 999,
})
excelFile, err := c.FormFile("excel")
if err != nil {
service.Error(c, err)
c.JSON(400, gin.H{"error": "缺少 Excel 文件 excel"})
return
}
if len(list.Data) == 0 {
service.Error(c, errors.New("没有待确认的作品"))
// 2. 保存临时文件
tempDir := "tmp"
os.MkdirAll(tempDir, 0755)
excelPath := filepath.Join(tempDir, "artists.xlsx")
if err = c.SaveUploadedFile(excelFile, excelPath); err != nil {
c.JSON(500, gin.H{"error": "保存 Excel 失败"})
return
}
defer os.RemoveAll(tempDir)
// 3. 读取 Excel 视频发布信息
artists, err := readCastWorkList(excelPath)
if err != nil {
c.JSON(500, gin.H{"error": "读取 Excel 失败"})
return
}
//遍历更新状态
var failedRecords []FailedRecord
for _, v := range list.Data {
res, err := service.AccountFieeProvider.UserList(context.Background(), &account.UserListRequest{
Name: v.ArtistName,
})
for _, v := range artists {
artistId, err := strconv.ParseUint(v.ArtistId, 10, 32)
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: v.ArtistName,
Msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()),
Msg: fmt.Sprintf("解析用户ID失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
log.Printf(fmt.Sprintf("解析用户ID失败: %s", err.Error()))
continue
}
var artistId uint64
if res != nil && len(res.UserList) > 0 {
artistId = res.UserList[0].Id
}
_, err = service.BundleProvider.AddBundleBalance(c, &bundle.AddBundleBalanceReq{
UserId: int32(artistId),
VideoConsumptionNumber: 1,
@ -64,6 +65,7 @@ func WorkConfirm(c *gin.Context) { // 确认作品并扣除余量
WorkUuid: v.WorkUuid,
ConfirmRemark: "",
ConfirmStatus: 1,
AutoPublish: apiCast.AutoPublishENUM_AutoPublish_FALSE,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
@ -76,3 +78,30 @@ func WorkConfirm(c *gin.Context) { // 确认作品并扣除余量
}
service.Success(c, failedRecords)
}
func readCastWorkList(excelPath string) ([]ArtistVideoDetail, error) {
f, err := excelize.OpenFile(excelPath)
if err != nil {
return nil, err
}
defer f.Close()
sheetName := f.GetSheetName(0)
rows, err := f.GetRows(sheetName)
if err != nil {
return nil, err
}
log.Println("start read excel...")
var artistVideos []ArtistVideoDetail
for i, row := range rows {
if i == 0 || len(row) < 2 {
continue
}
tmp := ArtistVideoDetail{
ArtistId: strings.TrimSpace(row[0]),
ArtistName: strings.TrimSpace(row[1]),
WorkUuid: strings.TrimSpace(row[2]),
}
artistVideos = append(artistVideos, tmp)
}
return artistVideos, nil
}

View File

@ -1,23 +1,18 @@
package imports
import (
"bytes"
"context"
"encoding/json"
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/bundle"
apiCast "fonchain-fiee/api/cast"
"fonchain-fiee/api/files"
"fonchain-fiee/pkg/config"
"fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/model"
modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/cast"
"fonchain-fiee/pkg/service/upload"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
@ -47,16 +42,18 @@ func ImportPublish(c *gin.Context) {
os.MkdirAll(tempDir, 0755)
excelPath := filepath.Join(tempDir, "artists.xlsx")
zipPath := filepath.Join(tempDir, "archive.zip")
fmt.Println("before save excel...")
now := time.Now()
if err = c.SaveUploadedFile(excelFile, excelPath); err != nil {
c.JSON(500, gin.H{"error": "保存 Excel 失败"})
return
}
fmt.Println("save excel success", time.Since(now))
if err = c.SaveUploadedFile(zipFile, zipPath); err != nil {
c.JSON(500, gin.H{"error": "保存 ZIP 文件失败"})
return
}
fmt.Println("save zip success", time.Since(now))
// 3. 解压 ZIP
unzipPath := filepath.Join(tempDir, "unzipped")
if _, err = os.Stat(unzipPath); err == nil {
@ -66,6 +63,7 @@ func ImportPublish(c *gin.Context) {
return
}
}
fmt.Println("开始解压...")
os.MkdirAll(unzipPath, 0755)
if err = archiver.Unarchive(zipPath, unzipPath); err != nil {
c.JSON(500, gin.H{"error": "解压 ZIP 失败: " + err.Error()})
@ -76,11 +74,11 @@ func ImportPublish(c *gin.Context) {
c.JSON(500, gin.H{"error": "读取解压目录失败或目录为空"})
return
}
if len(entries) == 1 && entries[0].IsDir() {
// 说明解压后多了一层目录,把它设为新的 unzipPath
unzipPath = filepath.Join(unzipPath, entries[0].Name())
}
fmt.Println("开始读取excel...")
defer os.RemoveAll(tempDir)
// 4. 读取 Excel 画家名单, 匹配视频和图片
artists, err := readArtistVideoInfo(excelPath, unzipPath)
@ -91,12 +89,12 @@ func ImportPublish(c *gin.Context) {
// 5.发布视频
var failedRecords []FailedRecord
var artistResp []ArtistVideoDetail
fmt.Println("artists num: ", len(artists))
for _, artist := range artists {
var infoResp *accountFiee.UserInfoResponse
var err error
list, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
Name: artist.Name,
Name: artist.Name,
SubNum: artist.SubNum,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
@ -106,6 +104,14 @@ func ImportPublish(c *gin.Context) {
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
continue
}
if len(list.UserList) == 0 {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("未找到用户信息: %s", artist.Name),
})
log.Printf(fmt.Sprintf("未找到用户信息: %s", artist.Name))
continue
}
if list != nil && len(list.UserList) > 0 {
infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{
ID: list.UserList[0].Id,
@ -131,10 +137,229 @@ func ImportPublish(c *gin.Context) {
}
//自媒体账号
accountList, err := service.CastProvider.MediaUserList(c, &apiCast.MediaUserListReq{
//ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistVal: artist.Name,
Page: 1,
PageSize: 10,
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistVal: artist.Name,
Page: 1,
PageSize: 10,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data)),
})
log.Printf(fmt.Sprintf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data)))
continue
}
if accountList == nil || len(accountList.Data) == 0 {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: "自媒体账号数量为0",
})
log.Printf(fmt.Sprintf("自媒体账号,账号数量:%d", len(accountList.Data)))
continue
}
mediaAccountUuids := []string{}
mediaAccountNames := []string{}
platformIDs := []apiCast.PlatformIDENUM{}
for _, info := range accountList.Data {
if info.PlatformID == 2 && ((artist.Id == "31" && info.ArtistName == "荣小松") ||
(artist.Id == "72" && info.ArtistName == "韩风霞")) {
continue // 跳过
}
mediaAccountUuids = append(mediaAccountUuids, info.MediaAccountUuid)
mediaAccountNames = append(mediaAccountNames, info.PlatformUserName)
platformIDs = append(platformIDs, apiCast.PlatformIDENUM(info.PlatformID))
}
newCtx := cast.NewCtxWithUserInfo(c)
resp, err := service.CastProvider.UpdateWorkVideo(newCtx, &apiCast.UpdateWorkVideoReq{
Title: artist.Title,
Content: artist.Title,
VideoUrl: artist.Video,
CoverUrl: artist.Img,
AutoPublish: apiCast.AutoPublishENUM_AutoPublish_FALSE,
MediaAccountUuids: mediaAccountUuids,
MediaAccountNames: mediaAccountNames,
PlatformIDs: platformIDs,
PublishConfig1: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 2,
IsAI: 1,
PublicType: 1,
},
PublishConfig2: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 2,
IsAI: 1,
PublicType: 1,
},
PublishConfig3: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 1,
IsAI: 1,
PublicType: 1,
},
Action: "submit",
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistName: infoResp.Name,
ArtistPhone: infoResp.TelNum,
ArtistPhoneAreaCode: infoResp.TelAreaCode,
Source: 2,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("发布"+artist.Name+"视频"+artist.Title+"失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("发布"+artist.Name+"视频"+artist.Title+"失败: %s", err.Error()))
continue
}
artistResp = append(artistResp, ArtistVideoDetail{
Id: artist.Id,
ArtistName: artist.Name,
SubNum: artist.SubNum,
Title: artist.Title,
WorkUuid: resp.WorkUuid,
Youtube: artist.Youtube,
Instagram: artist.Instagram,
TikTok: artist.TikTok,
})
}
// excelUrl, err := exportPublishRecordsToExcel(artistResp)
// if err != nil {
// service.Error(c, err)
// return
// }
// 6. 返回结果
service.Success(c, map[string]interface{}{
//"excelUrl": excelUrl,
"failedRecords": failedRecords,
})
}
func ImportPublishV2(c *gin.Context) {
// 1. 上传画家短视频详情文件
excelFile, err := c.FormFile("excel")
if err != nil {
c.JSON(400, gin.H{"error": "缺少 Excel 文件 excel"})
return
}
zipFile, err := c.FormFile("zip")
if err != nil {
c.JSON(400, gin.H{"error": "缺少 ZIP 文件"})
return
}
// 2. 保存临时文件
tempDir := "tmp"
os.MkdirAll(tempDir, 0755)
excelPath := filepath.Join(tempDir, "artists.xlsx")
zipPath := filepath.Join(tempDir, "archive.zip")
fmt.Println("before save excel...")
now := time.Now()
if err = c.SaveUploadedFile(excelFile, excelPath); err != nil {
c.JSON(500, gin.H{"error": "保存 Excel 失败"})
return
}
fmt.Println("save excel success", time.Since(now))
if err = c.SaveUploadedFile(zipFile, zipPath); err != nil {
c.JSON(500, gin.H{"error": "保存 ZIP 文件失败"})
return
}
fmt.Println("save zip success", time.Since(now))
// 3. 解压 ZIP
unzipPath := filepath.Join(tempDir, "unzipped")
if _, err = os.Stat(unzipPath); err == nil {
// 路径已存在,删除
if removeErr := os.RemoveAll(unzipPath); removeErr != nil {
c.JSON(500, gin.H{"error": "清理已存在解压目录失败: " + removeErr.Error()})
return
}
}
fmt.Println("开始解压...")
os.MkdirAll(unzipPath, 0755)
if err = archiver.Unarchive(zipPath, unzipPath); err != nil {
c.JSON(500, gin.H{"error": "解压 ZIP 失败: " + err.Error()})
return
}
entries, err := os.ReadDir(unzipPath)
if err != nil || len(entries) == 0 {
c.JSON(500, gin.H{"error": "读取解压目录失败或目录为空"})
return
}
fmt.Println("jieya后...")
if len(entries) == 1 && entries[0].IsDir() {
// 说明解压后多了一层目录,把它设为新的 unzipPath
unzipPath = filepath.Join(unzipPath, entries[0].Name())
}
fmt.Println("开始读取excel...")
defer os.RemoveAll(tempDir)
// 4. 读取 Excel 画家名单, 匹配视频和图片
artists, err := readArtistVideoInfo(excelPath, unzipPath)
if err != nil {
c.JSON(500, gin.H{"error": "读取 Excel 失败"})
return
}
// 5.发布视频
var failedRecords []FailedRecord
var artistResp []ArtistVideoDetail
fmt.Println("artists num: ", len(artists))
for _, artist := range artists {
var infoResp *accountFiee.UserInfoResponse
list, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
Name: artist.Name,
SubNum: artist.SubNum,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
continue
}
if len(list.UserList) == 0 {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("未找到用户信息: %s", artist.Name),
})
log.Printf(fmt.Sprintf("未找到用户信息: %s", artist.Name))
continue
}
if list != nil && len(list.UserList) > 0 {
infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{
ID: list.UserList[0].Id,
Domain: "app",
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
continue
}
}
if err = cast.CheckUserBundleBalance(int32(list.UserList[0].Id), modelCast.BalanceTypeVideoValue); err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("检查用户视频可消耗数量: %s", err.Error()),
})
log.Printf(fmt.Sprintf("检查用户视频可消耗数量: %s", err.Error()))
continue
}
//自媒体账号
accountList, err := service.CastProvider.MediaUserList(c, &apiCast.MediaUserListReq{
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistVal: artist.Name,
Page: 1,
PageSize: 10,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
@ -215,6 +440,7 @@ func ImportPublish(c *gin.Context) {
artistResp = append(artistResp, ArtistVideoDetail{
Id: artist.Id,
ArtistName: artist.Name,
SubNum: artist.SubNum,
Title: artist.Title,
WorkUuid: resp.WorkUuid,
Youtube: artist.Youtube,
@ -222,19 +448,18 @@ func ImportPublish(c *gin.Context) {
TikTok: artist.TikTok,
})
}
excelUrl, err := exportRecordsToExcel(artistResp)
if err != nil {
service.Error(c, err)
return
}
// excelUrl, err := exportPublishRecordsToExcel(artistResp)
// if err != nil {
// service.Error(c, err)
// return
// }
// 6. 返回结果
service.Success(c, map[string]interface{}{
"excelUrl": excelUrl,
//"excelUrl": excelUrl,
"failedRecords": failedRecords,
})
}
func readArtistVideoInfo(excelPath, unzipPath string) ([]ArtistMedia, error) {
log.Println(unzipPath)
f, err := excelize.OpenFile(excelPath)
@ -253,9 +478,6 @@ func readArtistVideoInfo(excelPath, unzipPath string) ([]ArtistMedia, error) {
if i == 0 || i == 1 || len(row) < 2 {
continue
}
if i == 165 {
break
}
id, _ := f.GetCellValue(sheetName, fmt.Sprintf("A%d", i+1))
if id != "" {
id = strings.TrimSpace(id)
@ -280,6 +502,10 @@ func readArtistVideoInfo(excelPath, unzipPath string) ([]ArtistMedia, error) {
if tiktok != "" {
tiktok = strings.TrimSpace(tiktok)
}
subNum, _ := f.GetCellValue(sheetName, fmt.Sprintf("G%d", i+1))
if subNum != "" {
subNum = strings.TrimSpace(subNum)
}
artists = append(artists, ArtistMedia{
Id: id,
Name: artistName,
@ -287,6 +513,7 @@ func readArtistVideoInfo(excelPath, unzipPath string) ([]ArtistMedia, error) {
Youtube: youtube,
Instagram: instagram,
TikTok: tiktok,
SubNum: subNum,
})
}
artists, err = matchArtistMedia(artists, unzipPath)
@ -296,13 +523,28 @@ func matchArtistMedia(artists []ArtistMedia, unzipPath string) ([]ArtistMedia, e
var err error
var res []ArtistMedia
for _, artist := range artists {
oldImgPath := fmt.Sprintf("%s/%s/%s.jpg", unzipPath, artist.Name, artist.Id)
oldVideoPath := fmt.Sprintf("%s/%s/%s.mp4", unzipPath, artist.Name, artist.Id)
var oldVideoPath, oldImgPath string
for _, ext := range []string{".jpg", ".png", ".jpeg"} {
p := fmt.Sprintf("%s/%s/%s%s", unzipPath, artist.Name, artist.Id, ext)
if _, err = os.Stat(p); err == nil {
oldImgPath = p
break
}
}
// 检查源文件是否存在
if _, err = os.Stat(oldImgPath); os.IsNotExist(err) {
fmt.Println("图片不存在: ", artist.Id, artist.Name, oldImgPath)
continue
}
if _, err = os.Stat(oldVideoPath); os.IsNotExist(err) {
for _, ext := range []string{".mp4", ".mov"} {
p := fmt.Sprintf("%s/%s/%s%s", unzipPath, artist.Name, artist.Id, ext)
if _, err = os.Stat(p); err == nil {
oldVideoPath = p
break
}
}
if oldVideoPath == "" {
fmt.Println("视频不存在: ", artist.Id, artist.Name, oldVideoPath)
continue
}
baseDir := filepath.Join(unzipPath, artist.Name)
@ -334,12 +576,12 @@ func matchArtistMedia(artists []ArtistMedia, unzipPath string) ([]ArtistMedia, e
return nil, err
}
var httpType string
if config.AppMode == "prod" {
url := "saas.fiee.com"
httpType = fmt.Sprintf("%s%s", model.HttpsType, url)
} else {
if config.AppConfig.System.AppMode == "dev" {
url := "114.218.158.24:9020"
httpType = fmt.Sprintf("%s%s", model.HttpType, url)
} else {
url := "saas.fiee.com"
httpType = fmt.Sprintf("%s%s", model.HttpsType, url)
}
baseUrl := fmt.Sprintf("%s/api/fiee/resource/raw/", httpType)
videoUrl := baseUrl + filepath.Base(videoPath)
@ -355,10 +597,12 @@ func matchArtistMedia(artists []ArtistMedia, unzipPath string) ([]ArtistMedia, e
tmp.Img = imgUrl
//tmp.Video = filepath.ToSlash(videoPath)
tmp.Video = videoUrl
tmp.SubNum = artist.SubNum
res = append(res, tmp)
}
return res, nil
}
func UploadToAnotherService(ctx context.Context, fileData []byte, path string) error {
const chunkSize = 4*1024*1024 - 100
_, err := service.FilesProvider.TusCreate(ctx, &files.TusCreateReq{
@ -392,148 +636,348 @@ func UploadToAnotherService(ctx context.Context, fileData []byte, path string) e
}
return nil
}
func exportRecordsToExcel(artistInfos []ArtistVideoDetail) (string, error) {
f := excelize.NewFile()
sheet := "Sheet1"
f.NewSheet(sheet)
// 写表头
headers := []string{"序号", "画家名", "标题", "uuid", "youtube", "instagram", "tiktok"}
// func exportPublishRecordsToExcel(artistInfos []ArtistVideoDetail) (string, error) {
// fileDir := "./runtime/import/"
// filename := "画家视频详情记录0922.xlsx"
// filePath := filepath.Join(fileDir, filename)
for col, h := range headers {
_ = f.SetCellValue(sheet, string(rune('A'+col))+"1", h)
}
// _ = os.MkdirAll(fileDir, os.ModePerm)
// 写数据
for i, artistInfo := range artistInfos {
row := i + 2
_ = f.SetCellValue(sheet, "A"+strconv.Itoa(row), artistInfo.Id)
_ = f.SetCellValue(sheet, "B"+strconv.Itoa(row), artistInfo.ArtistName)
_ = f.SetCellValue(sheet, "C"+strconv.Itoa(row), artistInfo.Title)
_ = f.SetCellValue(sheet, "D"+strconv.Itoa(row), artistInfo.WorkUuid)
_ = f.SetCellValue(sheet, "E"+strconv.Itoa(row), artistInfo.Youtube)
_ = f.SetCellValue(sheet, "F"+strconv.Itoa(row), artistInfo.Instagram)
_ = f.SetCellValue(sheet, "G"+strconv.Itoa(row), artistInfo.TikTok)
}
// 保存文件
filename := "画家视频详情记录.xlsx"
fileDir := "./runtime/import/" // 自定义目录
_ = os.MkdirAll(fileDir, os.ModePerm)
filePath := filepath.Join(fileDir, filename)
if err := f.SaveAs(filePath); err != nil {
return "", err
}
excelUrl, err := upload.PutBos(filePath, "excel", true)
// var f *excelize.File
// sheet := "Sheet1"
// // 判断文件是否存在
// if _, err := os.Stat(filePath); os.IsNotExist(err) {
// // 文件不存在新建文件和Sheet
// f = excelize.NewFile()
// f.SetSheetName(f.GetSheetName(0), sheet)
// // 写表头
// headers := []string{"序号", "画家名", "标题", "uuid", "youtube", "instagram", "tiktok", "用户编号"}
// for col, h := range headers {
// _ = f.SetCellValue(sheet, string('A'+col)+"1", h)
// }
// } else {
// // 文件存在,打开
// var err error
// f, err = excelize.OpenFile(filePath)
// if err != nil {
// return "", err
// }
// }
// // 找到最后一行,追加数据
// rows, err := f.GetRows(sheet)
// if err != nil {
// return "", err
// }
// // 计算下一行,从表头之后开始
// startRow := len(rows) + 1
// if startRow == 1 {
// startRow = 2 // 文件新建或没有数据从第2行开始
// }
// // 写数据
// for i, artistInfo := range artistInfos {
// row := startRow + i
// _ = f.SetCellValue(sheet, "A"+strconv.Itoa(row), row-1) // 序号连续
// _ = f.SetCellValue(sheet, "B"+strconv.Itoa(row), artistInfo.ArtistName)
// _ = f.SetCellValue(sheet, "C"+strconv.Itoa(row), artistInfo.Title)
// _ = f.SetCellValue(sheet, "D"+strconv.Itoa(row), artistInfo.WorkUuid)
// _ = f.SetCellValue(sheet, "E"+strconv.Itoa(row), artistInfo.Youtube)
// _ = f.SetCellValue(sheet, "F"+strconv.Itoa(row), artistInfo.Instagram)
// _ = f.SetCellValue(sheet, "G"+strconv.Itoa(row), artistInfo.TikTok)
// _ = f.SetCellValue(sheet, "H"+strconv.Itoa(row), artistInfo.SubNum)
// }
// // 保存文件
// if err = f.SaveAs(filePath); err != nil {
// fmt.Println("saveAs err: ", err)
// return "", err
// }
// // 上传
// excelUrl, err := upload.PutBos(filePath, "excel", false)
// if err != nil {
// return "", err
// }
// return excelUrl, nil
// }
func ImportPublishV3(c *gin.Context) {
// 1. 上传画家短视频详情文件
excelFile, err := c.FormFile("excel")
if err != nil {
return "", err
c.JSON(400, gin.H{"error": "缺少 Excel 文件 excel"})
return
}
return excelUrl, nil
}
func updateApproval(ctx *gin.Context, artistId uint64, workUuid string, accountInfos []*apiCast.MediaUserInfo, videoUrl, imgUrl, title string) error {
var publishAccounts []PublishAccount
var publishPlatformIds []int32
// 2. 保存临时文件
tempDir := "tmp"
os.MkdirAll(tempDir, 0755)
excelPath := filepath.Join(tempDir, "artists.xlsx")
for _, v := range accountInfos {
publishAccounts = append(publishAccounts, PublishAccount{
AccountName: v.PlatformUserName,
AccountID: v.MediaAccountUuid,
if err = c.SaveUploadedFile(excelFile, excelPath); err != nil {
c.JSON(500, gin.H{"error": "保存 Excel 失败"})
return
}
fmt.Println("save excel...")
defer os.RemoveAll(tempDir)
// 3. 读取 Excel 画家名单, 匹配视频和图片
artists, err := readArtistVideoInfoV2(c, excelPath)
if err != nil {
c.JSON(500, gin.H{"error": "读取 Excel 失败"})
return
}
// 4.发布视频
var failedRecords []FailedRecord
//var artistResp []ArtistVideoDetail
for _, artist := range artists {
var infoResp *accountFiee.UserInfoResponse
list, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
Name: artist.Name,
SubNum: artist.SubNum,
})
publishPlatformIds = append(publishPlatformIds, int32(v.PlatformID))
}
var req CreateRequest
var url string
saasPublishVideo := &SaasPublishVideo{
Title: title,
Describe: title,
IsYoutubeSee: 1,
IsTiktokScreen: 1,
IsTiktokComment: 1,
IsTiktokQuote: 1,
IsTiktokAiGenerate: 1,
Cover: imgUrl,
PlatformIds: publishPlatformIds,
PublishAccounts: publishAccounts,
Videos: []Video{
{
VideoAddress: videoUrl,
VideoThumbnail: imgUrl,
},
},
}
if config.AppMode == "prod" {
url = "https://erp.fonchain.com/approval/v2/create"
req = CreateRequest{
Type: "SaasPublishVideo",
DepartmentID: 3,
Domain: "7bfa3942cceb20389822af7b57c5798e",
MenuType: 2,
SaasPublishVideo: saasPublishVideo,
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
continue
}
} else {
url = "http://114.218.158.24:9020/approval/v2/create"
req = CreateRequest{
Type: "SaasPublishVideo",
DepartmentID: 3,
Domain: "7bfa3942cceb20389822af7b57c5798e",
MenuType: 2,
SaasPublishVideo: saasPublishVideo,
if len(list.UserList) == 0 {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("未找到用户信息: %s", artist.Name),
})
log.Printf(fmt.Sprintf("未找到用户信息: %s", artist.Name))
continue
}
if list != nil && len(list.UserList) > 0 {
infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{
ID: list.UserList[0].Id,
Domain: "app",
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error()))
continue
}
}
}
jsonBytes, err := json.Marshal(req)
if err != nil {
return err
if err = cast.CheckUserBundleBalance(int32(list.UserList[0].Id), modelCast.BalanceTypeVideoValue); err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("检查用户视频可消耗数量: %s", err.Error()),
})
log.Printf(fmt.Sprintf("检查用户视频可消耗数量: %s", err.Error()))
continue
}
//自媒体账号
accountList, err := service.CastProvider.MediaUserList(c, &apiCast.MediaUserListReq{
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistVal: artist.Name,
Page: 1,
PageSize: 10,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data)),
})
log.Printf(fmt.Sprintf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data)))
continue
}
if accountList == nil || len(accountList.Data) == 0 {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: "自媒体账号数量为0",
})
log.Printf(fmt.Sprintf("自媒体账号,账号数量:%d", len(accountList.Data)))
continue
}
mediaAccountUuids := []string{}
mediaAccountNames := []string{}
platformIDs := []apiCast.PlatformIDENUM{}
for _, info := range accountList.Data {
mediaAccountUuids = append(mediaAccountUuids, info.MediaAccountUuid)
mediaAccountNames = append(mediaAccountNames, info.PlatformUserName)
platformIDs = append(platformIDs, apiCast.PlatformIDENUM(info.PlatformID))
}
newCtx := cast.NewCtxWithUserInfo(c)
_, err = service.CastProvider.UpdateWorkVideo(newCtx, &apiCast.UpdateWorkVideoReq{
Title: artist.Title,
Content: artist.Title,
VideoUrl: artist.Video,
CoverUrl: artist.Img,
MediaAccountUuids: mediaAccountUuids,
MediaAccountNames: mediaAccountNames,
PlatformIDs: platformIDs,
PublishConfig1: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 2,
IsAI: 1,
PublicType: 1,
},
PublishConfig2: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 2,
IsAI: 1,
PublicType: 1,
},
PublishConfig3: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 1,
IsAI: 1,
PublicType: 1,
},
Action: "submit",
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistName: infoResp.Name,
ArtistPhone: infoResp.TelNum,
ArtistPhoneAreaCode: infoResp.TelAreaCode,
Source: 2,
})
if err != nil {
failedRecords = append(failedRecords, FailedRecord{
Name: artist.Name,
Msg: fmt.Sprintf("发布"+artist.Name+"视频"+artist.Title+"失败: %s", err.Error()),
})
log.Printf(fmt.Sprintf("发布"+artist.Name+"视频"+artist.Title+"失败: %s", err.Error()))
continue
}
// artistResp = append(artistResp, ArtistVideoDetail{
// Id: artist.Id,
// ArtistName: artist.Name,
// SubNum: artist.SubNum,
// Title: artist.Title,
// WorkUuid: resp.WorkUuid,
// Youtube: artist.Youtube,
// Instagram: artist.Instagram,
// TikTok: artist.TikTok,
// })
}
res, err := http.Post(url, "application/json", bytes.NewBuffer(jsonBytes))
if err != nil {
return err
}
defer res.Body.Close()
responseBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return err
}
var apiResp APIResponse
err = json.Unmarshal(responseBodyBytes, &apiResp)
if err != nil {
// 处理错误
return err
}
fmt.Println("拿到审批ID:", apiResp.Data.ID)
_, err = service.CastProvider.UpdateStatus(ctx, &apiCast.UpdateStatusReq{
WorkUuid: workUuid,
WorkAction: apiCast.WorkActionENUM_APPROVAL,
ApprovalID: strconv.FormatUint(apiResp.Data.ID, 10),
// excelUrl, err := exportPublishRecordsToExcel(artistResp)
// if err != nil {
// service.Error(c, err)
// return
// }
// 6. 返回结果
service.Success(c, map[string]interface{}{
//"excelUrl": excelUrl,
"failedRecords": failedRecords,
})
if err != nil {
return err
}
_, err = service.CastProvider.UpdateStatus(ctx, &apiCast.UpdateStatusReq{
WorkUuid: workUuid,
WorkAction: apiCast.WorkActionENUM_APPROVAL_PASS,
ApprovalID: strconv.FormatUint(apiResp.Data.ID, 10),
})
if err != nil {
return err
}
_, err = service.BundleProvider.AddBundleBalance(ctx, &bundle.AddBundleBalanceReq{
UserId: int32(artistId),
VideoConsumptionNumber: 1,
})
if err != nil {
log.Printf(fmt.Sprintf("扣除余额失败: %s", err.Error()))
return err
}
_, err = service.CastProvider.UpdateStatus(ctx, &apiCast.UpdateStatusReq{
WorkAction: apiCast.WorkActionENUM_CONFIRM,
WorkUuid: workUuid,
ConfirmRemark: "",
ConfirmStatus: 1,
})
if err != nil {
log.Printf(fmt.Sprintf("更新状态失败: %s", err.Error()))
return err
}
return nil
}
func readArtistVideoInfoV2(ctx *gin.Context, excelPath string) ([]ArtistMedia, error) {
f, err := excelize.OpenFile(excelPath)
if err != nil {
return nil, err
}
defer f.Close()
sheetName := f.GetSheetName(0)
rows, err := f.GetRows(sheetName)
if err != nil {
return nil, err
}
log.Println("start read excel")
var artists []ArtistMedia
for i, row := range rows {
if i == 0 || i == 1 || len(row) < 2 {
continue
}
id, _ := f.GetCellValue(sheetName, fmt.Sprintf("A%d", i+1))
if id != "" {
id = strings.TrimSpace(id)
}
artistName, _ := f.GetCellValue(sheetName, fmt.Sprintf("B%d", i+1))
if artistName != "" {
artistName = strings.TrimSpace(artistName)
}
title, _ := f.GetCellValue(sheetName, fmt.Sprintf("C%d", i+1))
if title != "" {
title = strings.TrimSpace(title)
}
subNum, _ := f.GetCellValue(sheetName, fmt.Sprintf("G%d", i+1))
if subNum != "" {
subNum = strings.TrimSpace(subNum)
}
artists = append(artists, ArtistMedia{
Id: id,
Name: artistName,
Title: title,
SubNum: subNum,
})
}
artists, err = matchArtistMediaV3(ctx, artists)
return artists, nil
}
func matchArtistMediaV3(ctx *gin.Context, artists []ArtistMedia) ([]ArtistMedia, error) {
var medias = make(map[string][]*files.Items)
var res []ArtistMedia
for _, artist := range artists {
mediaPath := "fiee2/" + artist.Name
imgPath := fmt.Sprintf("%s/%s/%s.jpg", mediaPath, artist.Name, artist.Title)
videoPath := fmt.Sprintf("%s/%s/%s.mp4", mediaPath, artist.Name, artist.Title)
//判断文件是否存在
if _, ok := medias[artist.Name]; !ok {
sortBy := ctx.DefaultQuery("sortBy", "name")
sortAsc, _ := strconv.ParseBool(ctx.DefaultQuery("sortAsc", "true"))
resp, err := service.FilesProvider.List(ctx, &files.FileListReq{
Path: mediaPath,
UserSpacePath: "",
Sorting: &files.Sorting{
By: sortBy,
Asc: sortAsc,
},
})
if err != nil {
return nil, err
}
medias[artist.Name] = resp.Items
}
isExist := false
for _, media := range medias[artist.Name] {
if media.Name == artist.Title+".jpg" || media.Name == artist.Title+".mp4" || media.Name == artist.Title+".mov" {
isExist = true
break
}
}
if !isExist {
continue
}
var httpType string
if config.AppConfig.System.AppMode == "dev" {
url := "114.218.158.24:9020"
httpType = fmt.Sprintf("%s%s", model.HttpType, url)
} else {
url := "saas.fiee.com"
httpType = fmt.Sprintf("%s%s", model.HttpsType, url)
}
baseUrl := fmt.Sprintf("%s/api/fiee/resource/raw/", httpType)
videoUrl := baseUrl + filepath.Base(videoPath)
imgUrl := baseUrl + filepath.Base(imgPath)
tmp := artist
tmp.Id = artist.Id
tmp.Name = artist.Name
tmp.Title = artist.Title
tmp.Img = imgUrl
tmp.Video = videoUrl
tmp.SubNum = artist.SubNum
res = append(res, tmp)
}
return res, nil
}

View File

@ -7,6 +7,9 @@
package stime
import (
"errors"
"fonchain-fiee/pkg/e"
"go.uber.org/zap"
"time"
)
@ -88,3 +91,19 @@ var WeekStrMap = map[string]string{
"Saturday": "六",
"Sunday": "日",
}
func DatetimeToTimes(datetime string, dateFormat string) (times int32, err error) {
if datetime == "" {
times = 0
return
}
loc, _ := time.LoadLocation("Asia/Shanghai")
t, err := time.ParseInLocation(dateFormat, datetime, loc)
if err != nil {
zap.L().Error("DatetimeToTimes err:"+datetime+":", zap.Error(err))
err = errors.New(e.GetCodeMsg(e.InvalidParams))
return
}
times = int32(t.Unix())
return
}

View File

@ -7,14 +7,16 @@ import (
"errors"
"fmt"
"fonchain-fiee/pkg/e"
"github.com/gin-gonic/gin"
"github.com/tealeg/xlsx"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"time"
"github.com/gin-gonic/gin"
"github.com/tealeg/xlsx"
)
const (
@ -128,3 +130,36 @@ func ResponseXls(c *gin.Context, content io.ReadSeeker, fileTag string) {
//c.Writer.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
http.ServeContent(c.Writer, c.Request, fileName, time.Now(), content)
}
func CreateDirPath(path string) (err error) {
if _, err = os.Stat(path); os.IsNotExist(err) {
if err = os.MkdirAll(path, os.ModePerm); err != nil {
return
}
}
return
}
func CopyFile(src, dstDir string) (string, error) {
in, err := os.Open(src)
if err != nil {
return "", err
}
defer in.Close()
if err := os.MkdirAll(dstDir, 0755); err != nil {
return "", err
}
dst := filepath.Join(dstDir, filepath.Base(src))
out, err := os.Create(dst)
if err != nil {
return "", err
}
defer out.Close()
if _, err = io.Copy(out, in); err != nil {
return "", err
}
if err = out.Sync(); err != nil {
return "", err
}
return dst, nil
}