pasture_service.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. package backend
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "kpt-tmr-group/model"
  7. "kpt-tmr-group/pkg/xerr"
  8. operationPb "kpt-tmr-group/proto/go/backend/operation"
  9. "net/http"
  10. "github.com/gin-gonic/gin"
  11. "gorm.io/gorm"
  12. )
  13. var (
  14. CattleParentCategoryMap = map[operationPb.CattleCategoryParent_Kind]string{
  15. operationPb.CattleCategoryParent_LACTATION_CAW: "泌乳牛",
  16. operationPb.CattleCategoryParent_FATTEN_CAW: "育肥牛",
  17. operationPb.CattleCategoryParent_RESERVE_CAW: "后备牛",
  18. operationPb.CattleCategoryParent_DRY_CAW: "干奶牛",
  19. operationPb.CattleCategoryParent_PERINATAL_CAW: "围产牛",
  20. operationPb.CattleCategoryParent_OTHER_CAW: "其他",
  21. }
  22. ForageParentCategoryMap = map[operationPb.ForageCategoryParent_Kind]string{
  23. operationPb.ForageCategoryParent_ROUGHAGE: "粗料",
  24. operationPb.ForageCategoryParent_CONCENTRATE: "精料",
  25. operationPb.ForageCategoryParent_HALF_ROUGHAGE_HALF_CONCENTRATE: "粗料精料各半",
  26. operationPb.ForageCategoryParent_OTHER: "其他",
  27. }
  28. ForageSourceMap = map[operationPb.ForageSource_Kind]string{
  29. operationPb.ForageSource_SYSTEM_BUILT_IN: "系统内置",
  30. operationPb.ForageSource_USER_DEFINED: "用户自定义",
  31. }
  32. ForagePlanTypeMap = map[operationPb.ForagePlanType_Kind]string{
  33. operationPb.ForagePlanType_INVALID: "无",
  34. operationPb.ForagePlanType_FORKLIFT: "铲车",
  35. operationPb.ForagePlanType_CONCENTRATE: "精料",
  36. }
  37. JumpDelaTypeMap = map[operationPb.JumpDelaType_Kind]string{
  38. operationPb.JumpDelaType_INVALID: "禁用",
  39. operationPb.JumpDelaType_THREE: "3秒",
  40. operationPb.JumpDelaType_SIX: "6秒",
  41. operationPb.JumpDelaType_NINE: "9秒",
  42. }
  43. IsShowMap = map[operationPb.IsShow_Kind]string{
  44. operationPb.IsShow_OK: "是",
  45. operationPb.IsShow_NO: "否",
  46. }
  47. )
  48. type ForageEnumList struct {
  49. ForageSource map[operationPb.ForageSource_Kind]string `json:"forage_source"`
  50. ForagePlanType map[operationPb.ForagePlanType_Kind]string `json:"forage_plan_type"`
  51. JumpDelaType map[operationPb.JumpDelaType_Kind]string `json:"jump_dela_type"`
  52. CattleParentCategory map[operationPb.CattleCategoryParent_Kind]string `json:"cattle_parent_category"`
  53. ForageParentCategory map[operationPb.ForageCategoryParent_Kind]string `json:"forage_parent_category"`
  54. IsShow map[operationPb.IsShow_Kind]string `json:"is_show"`
  55. }
  56. // CreateGroupPasture 创建集团牧场
  57. func (s *StoreEntry) CreateGroupPasture(ctx context.Context, req *operationPb.AddPastureRequest) error {
  58. pastureList := model.NewGroupPasture(req)
  59. if err := s.DB.Create(pastureList).Error; err != nil {
  60. return xerr.WithStack(err)
  61. }
  62. return nil
  63. }
  64. // EditGroupPasture 创建集团牧场
  65. func (s *StoreEntry) EditGroupPasture(ctx context.Context, req *operationPb.AddPastureRequest) error {
  66. groupPasture := &model.GroupPasture{Id: req.Id}
  67. if err := s.DB.First(groupPasture).Error; err != nil {
  68. if errors.Is(err, gorm.ErrRecordNotFound) {
  69. return xerr.Custom("该数据不存在!")
  70. }
  71. return xerr.WithStack(err)
  72. }
  73. updateData := &model.GroupPasture{
  74. Name: req.Name,
  75. Account: req.Account,
  76. ManagerUser: req.ManagerUser,
  77. ManagerPhone: req.ManagerPhone,
  78. Address: req.Address,
  79. }
  80. if err := s.DB.Model(new(model.GroupPasture)).Omit("is_show", "password").
  81. Where("id = ?", req.Id).
  82. Updates(updateData).Error; err != nil {
  83. return xerr.WithStack(err)
  84. }
  85. return nil
  86. }
  87. // SearchGroupPastureList 查询牧场列表
  88. func (s *StoreEntry) SearchGroupPastureList(ctx context.Context, req *operationPb.SearchPastureRequest) (*operationPb.SearchPastureResponse, error) {
  89. groupPasture := make([]*model.GroupPasture, 0)
  90. var count int64 = 0
  91. pref := s.DB.Model(new(model.GroupPasture)).Where("is_delete = ? ", operationPb.IsShow_OK)
  92. if req.Name != "" {
  93. pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
  94. }
  95. if req.ManagerPhone != "" {
  96. pref.Where("manager_phone like ?", fmt.Sprintf("%s%s%s", "%", req.ManagerPhone, "%"))
  97. }
  98. if req.ManagerUser != "" {
  99. pref.Where("manager_user like ?", fmt.Sprintf("%s%s%s", "%", req.ManagerUser, "%"))
  100. }
  101. if req.StartTime > 0 && req.EndTime > 0 && req.EndTime >= req.StartTime {
  102. pref.Where("created_at BETWEEN ? AND ? ", req.StartTime, req.EndTime)
  103. }
  104. if err := pref.Order("id desc").Count(&count).Limit(int(req.Pagination.PageSize)).Offset(int(req.Pagination.PageOffset)).
  105. Find(&groupPasture).Debug().Error; err != nil {
  106. return nil, xerr.WithStack(err)
  107. }
  108. return &operationPb.SearchPastureResponse{
  109. Code: http.StatusOK,
  110. Msg: "ok",
  111. Data: &operationPb.SearchPastureData{
  112. Page: req.Pagination.Page,
  113. Total: int32(count),
  114. List: model.GroupPastureSlice(groupPasture).ToPB(),
  115. },
  116. }, nil
  117. }
  118. func (s *StoreEntry) DeleteGroupPasture(ctx context.Context, pastureId int64) error {
  119. groupPasture := &model.GroupPasture{
  120. Id: pastureId,
  121. }
  122. if err := s.DB.First(groupPasture).Error; err != nil {
  123. if errors.Is(err, gorm.ErrRecordNotFound) {
  124. return xerr.Custom("该数据不存在")
  125. }
  126. return xerr.WithStack(err)
  127. }
  128. if err := s.DB.Model(groupPasture).Update("is_delete", operationPb.IsShow_NO).Error; err != nil {
  129. return xerr.WithStack(err)
  130. }
  131. return nil
  132. }
  133. func (s *StoreEntry) ResetPasswordGroupPasture(ctx context.Context, req *operationPb.RestPasswordGroupPasture) error {
  134. groupPasture := &model.GroupPasture{
  135. Id: req.PastureId,
  136. }
  137. if err := s.DB.First(groupPasture).Error; err != nil {
  138. if errors.Is(err, gorm.ErrRecordNotFound) {
  139. return xerr.Custom("该数据不存在")
  140. }
  141. return xerr.WithStack(err)
  142. }
  143. if err := s.DB.Model(groupPasture).Update("password", req.Password).Error; err != nil {
  144. return xerr.WithStack(err)
  145. }
  146. return nil
  147. }
  148. func (s *StoreEntry) IsShowGroupPasture(ctx context.Context, req *operationPb.IsShowGroupPasture) error {
  149. groupPasture := &model.GroupPasture{
  150. Id: req.PastureId,
  151. }
  152. if err := s.DB.First(groupPasture).Error; err != nil {
  153. if errors.Is(err, gorm.ErrRecordNotFound) {
  154. return xerr.Custom("该数据不存在")
  155. }
  156. return xerr.WithStack(err)
  157. }
  158. if err := s.DB.Model(groupPasture).Update("is_show", req.IsShow).Error; err != nil {
  159. return xerr.WithStack(err)
  160. }
  161. return nil
  162. }
  163. // ParentCattleCategoryList 畜牧类别父类列表
  164. func (s *StoreEntry) ParentCattleCategoryList(ctx context.Context) map[operationPb.CattleCategoryParent_Kind]string {
  165. return CattleParentCategoryMap
  166. }
  167. // AddCattleCategory 添加畜牧分类
  168. func (s *StoreEntry) AddCattleCategory(ctx context.Context, req *operationPb.AddCattleCategoryRequest) error {
  169. cattleCategory := model.NewCattleCategory(req)
  170. if err := s.DB.Create(cattleCategory).Error; err != nil {
  171. return xerr.WithStack(err)
  172. }
  173. return nil
  174. }
  175. // EditCattleCategory 编辑畜牧分类
  176. func (s *StoreEntry) EditCattleCategory(ctx context.Context, req *operationPb.AddCattleCategoryRequest) error {
  177. cattleCategory := &model.CattleCategory{Id: req.Id}
  178. if err := s.DB.First(cattleCategory).Error; err != nil {
  179. if errors.Is(err, gorm.ErrRecordNotFound) {
  180. return xerr.Custom("该数据不存在")
  181. }
  182. return xerr.WithStack(err)
  183. }
  184. updateData := &model.CattleCategory{
  185. ParentName: req.ParentName,
  186. Name: req.Name,
  187. Number: req.Number,
  188. ParentId: req.ParentId,
  189. }
  190. if err := s.DB.Model(new(model.CattleCategory)).Omit("is_show", "is_delete").
  191. Where("id = ?", req.Id).
  192. Updates(updateData).Error; err != nil {
  193. return xerr.WithStack(err)
  194. }
  195. return nil
  196. }
  197. // IsShowCattleCategory 是否启用
  198. func (s *StoreEntry) IsShowCattleCategory(ctx context.Context, req *operationPb.IsShowCattleCategory) error {
  199. cattleCategory := &model.CattleCategory{Id: req.CattleCategoryId}
  200. if err := s.DB.First(cattleCategory).Error; err != nil {
  201. if errors.Is(err, gorm.ErrRecordNotFound) {
  202. return xerr.Custom("该数据不存在")
  203. }
  204. return xerr.WithStack(err)
  205. }
  206. if err := s.DB.Model(new(model.CattleCategory)).Where("id = ?", req.CattleCategoryId).Update("is_show", req.IsShow).Error; err != nil {
  207. return xerr.WithStack(err)
  208. }
  209. return nil
  210. }
  211. // DeleteCattleCategory 是否删除
  212. func (s *StoreEntry) DeleteCattleCategory(ctx context.Context, cattleCategoryId int64) error {
  213. cattleCategory := &model.CattleCategory{Id: cattleCategoryId}
  214. if err := s.DB.First(cattleCategory).Error; err != nil {
  215. if errors.Is(err, gorm.ErrRecordNotFound) {
  216. return xerr.Custom("该数据不存在")
  217. }
  218. return xerr.WithStack(err)
  219. }
  220. if err := s.DB.Model(new(model.CattleCategory)).Where("id = ?", cattleCategoryId).Update("is_delete", operationPb.IsShow_NO).Error; err != nil {
  221. return xerr.WithStack(err)
  222. }
  223. return nil
  224. }
  225. // SearchCattleCategoryList 牧畜分类类别列表
  226. func (s *StoreEntry) SearchCattleCategoryList(ctx context.Context, req *operationPb.SearchCattleCategoryRequest) (*operationPb.SearchCattleCategoryResponse, error) {
  227. cattleCategory := make([]*model.CattleCategory, 0)
  228. var count int64 = 0
  229. pref := s.DB.Model(new(model.CattleCategory)).Where("is_delete = ?", operationPb.IsShow_OK)
  230. if req.Name != "" {
  231. pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
  232. }
  233. if req.ParentName != "" {
  234. pref.Where("parent_name like ?", fmt.Sprintf("%s%s%s", "%", req.ParentName, "%"))
  235. }
  236. if req.IsShow > 0 {
  237. pref.Where("is_show = ?", req.IsShow)
  238. }
  239. if err := pref.Order("id desc").Count(&count).Limit(int(req.Pagination.PageSize)).Offset(int(req.Pagination.PageOffset)).
  240. Find(&cattleCategory).Debug().Error; err != nil {
  241. return nil, xerr.WithStack(err)
  242. }
  243. return &operationPb.SearchCattleCategoryResponse{
  244. Code: http.StatusOK,
  245. Msg: "ok",
  246. Data: &operationPb.SearchCattleCategoryData{
  247. Page: req.Pagination.Page,
  248. Total: int32(count),
  249. List: model.CattleCategorySlice(cattleCategory).ToPB(),
  250. },
  251. }, nil
  252. }
  253. // ParentForageCategoryList 饲料类别父类列表
  254. func (s *StoreEntry) ParentForageCategoryList(ctx context.Context) map[operationPb.ForageCategoryParent_Kind]string {
  255. return ForageParentCategoryMap
  256. }
  257. // AddForageCategory 添加饲料分类
  258. func (s *StoreEntry) AddForageCategory(ctx context.Context, req *operationPb.AddForageCategoryRequest) error {
  259. forageCategory := model.NewForageCategory(req)
  260. if err := s.DB.Create(forageCategory).Error; err != nil {
  261. return xerr.WithStack(err)
  262. }
  263. return nil
  264. }
  265. // EditForageCategory 编辑饲料分类
  266. func (s *StoreEntry) EditForageCategory(ctx context.Context, req *operationPb.AddForageCategoryRequest) error {
  267. forageCategory := &model.ForageCategory{Id: req.Id}
  268. if err := s.DB.First(forageCategory).Error; err != nil {
  269. if errors.Is(err, gorm.ErrRecordNotFound) {
  270. return xerr.Custom("该数据不存在")
  271. }
  272. return xerr.WithStack(err)
  273. }
  274. updateData := &model.ForageCategory{
  275. ParentName: req.ParentName,
  276. Name: req.Name,
  277. Number: req.Number,
  278. ParentId: req.ParentId,
  279. }
  280. if err := s.DB.Model(new(model.ForageCategory)).Omit("is_show", "is_delete").
  281. Where("id = ?", req.Id).
  282. Updates(updateData).Error; err != nil {
  283. return xerr.WithStack(err)
  284. }
  285. return nil
  286. }
  287. // IsShowForageCategory 是否启用
  288. func (s *StoreEntry) IsShowForageCategory(ctx context.Context, req *operationPb.IsShowForageCategory) error {
  289. forageCategory := &model.ForageCategory{Id: req.ForageCategoryId}
  290. if err := s.DB.First(forageCategory).Error; err != nil {
  291. if errors.Is(err, gorm.ErrRecordNotFound) {
  292. return xerr.Custom("该数据不存在")
  293. }
  294. return xerr.WithStack(err)
  295. }
  296. if err := s.DB.Model(new(model.ForageCategory)).Where("id = ?", req.ForageCategoryId).Update("is_show", req.IsShow).Error; err != nil {
  297. return xerr.WithStack(err)
  298. }
  299. return nil
  300. }
  301. // DeleteForageCategory 是否删除
  302. func (s *StoreEntry) DeleteForageCategory(ctx context.Context, forageCategoryId int64) error {
  303. forageCategory := &model.ForageCategory{Id: forageCategoryId}
  304. if err := s.DB.First(forageCategory).Error; err != nil {
  305. if errors.Is(err, gorm.ErrRecordNotFound) {
  306. return xerr.Custom("该数据不存在")
  307. }
  308. return xerr.WithStack(err)
  309. }
  310. if err := s.DB.Model(new(model.ForageCategory)).Where("id = ?", forageCategoryId).Update("is_delete", operationPb.IsShow_NO).Error; err != nil {
  311. return xerr.WithStack(err)
  312. }
  313. return nil
  314. }
  315. // SearchForageCategoryList 饲料分类类别列表
  316. func (s *StoreEntry) SearchForageCategoryList(ctx context.Context, req *operationPb.SearchForageCategoryRequest) (*operationPb.SearchForageCategoryResponse, error) {
  317. forageCategory := make([]*model.ForageCategory, 0)
  318. var count int64 = 0
  319. pref := s.DB.Model(new(model.ForageCategory)).Where("is_delete = ?", operationPb.IsShow_OK)
  320. if req.Name != "" {
  321. pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
  322. }
  323. if req.ParentName != "" {
  324. pref.Where("parent_name like ?", fmt.Sprintf("%s%s%s", "%", req.ParentName, "%"))
  325. }
  326. if req.IsShow > 0 {
  327. pref.Where("is_show = ?", req.IsShow)
  328. }
  329. if err := pref.Order("id desc").Count(&count).Limit(int(req.Pagination.PageSize)).Offset(int(req.Pagination.PageOffset)).
  330. Find(&forageCategory).Debug().Error; err != nil {
  331. return nil, xerr.WithStack(err)
  332. }
  333. return &operationPb.SearchForageCategoryResponse{
  334. Code: http.StatusOK,
  335. Msg: "ok",
  336. Data: &operationPb.SearchForageCategoryData{
  337. Page: req.Pagination.Page,
  338. Total: int32(count),
  339. List: model.ForageCategorySlice(forageCategory).ToPB(),
  340. },
  341. }, nil
  342. }
  343. // CreateForage 创建饲料
  344. func (s *StoreEntry) CreateForage(ctx context.Context, req *operationPb.AddForageRequest) error {
  345. forage := model.NewForage(req)
  346. if err := s.DB.Create(forage).Error; err != nil {
  347. return xerr.WithStack(err)
  348. }
  349. return nil
  350. }
  351. // EditForage 编辑饲料
  352. func (s *StoreEntry) EditForage(ctx context.Context, req *operationPb.AddForageRequest) error {
  353. forage := model.Forage{Id: req.Id}
  354. if err := s.DB.Where("is_delete = ?", operationPb.IsShow_OK).First(forage).Error; err != nil {
  355. if errors.Is(err, gorm.ErrRecordNotFound) {
  356. return xerr.Custom("该数据不存在")
  357. }
  358. return xerr.WithStack(err)
  359. }
  360. updateData := &model.Forage{
  361. Name: req.Name,
  362. CategoryId: req.CategoryId,
  363. UniqueEncode: req.UniqueEncode,
  364. ForageSourceId: req.ForageSourceId,
  365. PlanTypeId: req.PlanTypeId,
  366. SmallMaterialScale: req.SmallMaterialScale,
  367. AllowError: req.AllowError,
  368. PackageWeight: req.PackageWeight,
  369. Price: req.Price,
  370. JumpWeight: req.JumpWeight,
  371. JumpDelay: req.JumpDelay,
  372. ConfirmStart: req.ConfirmStart,
  373. RelayLocations: req.RelayLocations,
  374. Jmp: req.Jmp,
  375. Backup1: req.Backup1,
  376. Backup2: req.Backup2,
  377. Backup3: req.Backup3,
  378. }
  379. if err := s.DB.Model(new(model.Forage)).Omit("is_show", "is_delete").Where("id = ?", req.Id).
  380. Updates(updateData).Error; err != nil {
  381. return xerr.WithStack(err)
  382. }
  383. return nil
  384. }
  385. func (s *StoreEntry) SearchForageList(ctx context.Context, req *operationPb.SearchForageListRequest) (*operationPb.SearchForageListResponse, error) {
  386. forage := make([]*model.Forage, 0)
  387. var count int64 = 0
  388. pref := s.DB.Model(new(model.Forage)).Where("is_delete = ?", operationPb.IsShow_OK)
  389. if req.Name != "" {
  390. pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
  391. }
  392. if req.CategoryId != "" {
  393. pref.Where("category_id = ?", req.CategoryId)
  394. }
  395. if req.ForageSourceId > 0 {
  396. pref.Where("forage_source_id = ?", req.ForageSourceId)
  397. }
  398. if req.IsShow > 0 {
  399. pref.Where("is_show = ?", req.IsShow)
  400. }
  401. if req.AllowError > 0 {
  402. pref.Where("allow_error = ?", req.AllowError)
  403. }
  404. if req.AllowError > 0 {
  405. pref.Where("allow_error = ?", req.AllowError)
  406. }
  407. if req.JumpWeight > 0 {
  408. pref.Where("jump_weight = ?", req.JumpWeight)
  409. }
  410. if req.JumpDelay > 0 {
  411. pref.Where("jump_delay = ?", req.JumpDelay)
  412. }
  413. if err := pref.Order("id desc").Count(&count).Limit(int(req.Pagination.PageSize)).Offset(int(req.Pagination.PageOffset)).
  414. Find(&forage).Error; err != nil {
  415. return nil, xerr.WithStack(err)
  416. }
  417. return &operationPb.SearchForageListResponse{
  418. Page: req.Pagination.Page,
  419. Total: int32(count),
  420. List: model.ForageSlice(forage).ToPB(),
  421. }, nil
  422. }
  423. func (s *StoreEntry) ForageEnumList(ctx context.Context) *ForageEnumList {
  424. return &ForageEnumList{
  425. JumpDelaType: JumpDelaTypeMap,
  426. ForageSource: ForageSourceMap,
  427. ForagePlanType: ForagePlanTypeMap,
  428. CattleParentCategory: CattleParentCategoryMap,
  429. ForageParentCategory: ForageParentCategoryMap,
  430. IsShow: IsShowMap,
  431. }
  432. }
  433. func (s *StoreEntry) DeleteForageList(ctx context.Context, ids []int64) error {
  434. if len(ids) == 0 {
  435. return xerr.Custom("参数错误")
  436. }
  437. if err := s.DB.Where("id IN ?", ids).Update("is_delete", operationPb.IsShow_NO).Error; err != nil {
  438. return xerr.WithStack(err)
  439. }
  440. return nil
  441. }
  442. func (s *StoreEntry) IsShowForage(ctx context.Context, req *operationPb.IsShowForage) error {
  443. forage := &model.Forage{Id: req.ForageId}
  444. if err := s.DB.First(forage).Error; err != nil {
  445. if errors.Is(err, gorm.ErrRecordNotFound) {
  446. return xerr.Custom("该数据不存在")
  447. }
  448. return xerr.WithStack(err)
  449. }
  450. if err := s.DB.Model(new(model.Forage)).Where("id = ?", req.ForageId).Update("is_show", req.IsShow).Error; err != nil {
  451. return xerr.WithStack(err)
  452. }
  453. return nil
  454. }
  455. func (s *StoreEntry) ImportForage(ctx context.Context) error {
  456. return nil
  457. }
  458. func (s *StoreEntry) ExportForage(ctx *gin.Context, req *operationPb.SearchForageListRequest) error {
  459. res, err := s.SearchForageList(context.Background(), req)
  460. if err != nil {
  461. return xerr.WithStack(err)
  462. }
  463. if len(res.List) <= 0 {
  464. return nil
  465. }
  466. titleList := []string{
  467. "a",
  468. "b",
  469. "c",
  470. }
  471. data := make([]interface{}, 0)
  472. for _, v := range res.List {
  473. data = append(data, v)
  474. }
  475. return s.ExcelClient.ExportExcelByStruct(titleList, data, "demo", "sheet1", ctx)
  476. }