sys_auto_code.go 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. package system
  2. import (
  3. "archive/zip"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "mime/multipart"
  9. "os"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "text/template"
  14. ast2 "server/utils/ast"
  15. cp "github.com/otiai10/copy"
  16. "go.uber.org/zap"
  17. "server/resource/autocode_template/subcontract"
  18. "server/global"
  19. "server/model/system"
  20. "server/utils"
  21. "gorm.io/gorm"
  22. )
  23. const (
  24. autoPath = "autocode_template/"
  25. autocodePath = "resource/autocode_template"
  26. plugServerPath = "resource/plug_template/server"
  27. plugWebPath = "resource/plug_template/web"
  28. packageService = "service/%s/enter.go"
  29. packageServiceName = "service"
  30. packageRouter = "router/%s/enter.go"
  31. packageRouterName = "router"
  32. packageAPI = "api/v1/%s/enter.go"
  33. packageAPIName = "api/v1"
  34. )
  35. type autoPackage struct {
  36. path string
  37. temp string
  38. name string
  39. }
  40. var (
  41. packageInjectionMap map[string]astInjectionMeta
  42. injectionPaths []injectionMeta
  43. )
  44. func Init(Package string) {
  45. injectionPaths = []injectionMeta{
  46. {
  47. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  48. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, Package), "enter.go"),
  49. funcName: "ApiGroup",
  50. structNameF: "%sApi",
  51. },
  52. {
  53. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  54. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, Package), "enter.go"),
  55. funcName: "RouterGroup",
  56. structNameF: "%sRouter",
  57. },
  58. {
  59. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  60. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, Package), "enter.go"),
  61. funcName: "ServiceGroup",
  62. structNameF: "%sService",
  63. },
  64. }
  65. packageInjectionMap = map[string]astInjectionMeta{
  66. packageServiceName: {
  67. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  68. global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"),
  69. importCodeF: "server/%s/%s",
  70. packageNameF: "%s",
  71. groupName: "ServiceGroup",
  72. structNameF: "%sServiceGroup",
  73. },
  74. packageRouterName: {
  75. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  76. global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"),
  77. importCodeF: "server/%s/%s",
  78. packageNameF: "%s",
  79. groupName: "RouterGroup",
  80. structNameF: "%s",
  81. },
  82. packageAPIName: {
  83. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  84. global.GVA_CONFIG.AutoCode.Server, "api/v1", "enter.go"),
  85. importCodeF: "server/%s/%s",
  86. packageNameF: "%s",
  87. groupName: "ApiGroup",
  88. structNameF: "%sApiGroup",
  89. },
  90. }
  91. }
  92. type injectionMeta struct {
  93. path string
  94. funcName string
  95. structNameF string // 带格式化的
  96. }
  97. type astInjectionMeta struct {
  98. path string
  99. importCodeF string
  100. structNameF string
  101. packageNameF string
  102. groupName string
  103. }
  104. type tplData struct {
  105. template *template.Template
  106. autoPackage string
  107. locationPath string
  108. autoCodePath string
  109. autoMoveFilePath string
  110. }
  111. type AutoCodeService struct{}
  112. var AutoCodeServiceApp = new(AutoCodeService)
  113. // @author: [songzhibin97](https://github.com/songzhibin97)
  114. // @function: PreviewTemp
  115. // @description: 预览创建代码
  116. // @param: model.AutoCodeStruct
  117. // @return: map[string]string, error
  118. func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStruct) (map[string]string, error) {
  119. fmtField(&autoCode)
  120. dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
  121. if err != nil {
  122. return nil, err
  123. }
  124. // 写入文件前,先创建文件夹
  125. if err = utils.CreateDir(needMkdir...); err != nil {
  126. return nil, err
  127. }
  128. // 创建map
  129. ret := make(map[string]string)
  130. // 生成map
  131. for _, value := range dataList {
  132. ext := ""
  133. if ext = filepath.Ext(value.autoCodePath); ext == ".txt" {
  134. continue
  135. }
  136. f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
  137. if err != nil {
  138. return nil, err
  139. }
  140. if err = value.template.Execute(f, autoCode); err != nil {
  141. return nil, err
  142. }
  143. _ = f.Close()
  144. f, err = os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_RDONLY, 0o755)
  145. if err != nil {
  146. return nil, err
  147. }
  148. builder := strings.Builder{}
  149. builder.WriteString("```")
  150. if ext != "" && strings.Contains(ext, ".") {
  151. builder.WriteString(strings.Replace(ext, ".", "", -1))
  152. }
  153. builder.WriteString("\n\n")
  154. data, err := io.ReadAll(f)
  155. if err != nil {
  156. return nil, err
  157. }
  158. builder.Write(data)
  159. builder.WriteString("\n\n```")
  160. pathArr := strings.Split(value.autoCodePath, string(os.PathSeparator))
  161. ret[pathArr[1]+"-"+pathArr[3]] = builder.String()
  162. _ = f.Close()
  163. }
  164. defer func() { // 移除中间文件
  165. if err := os.RemoveAll(autoPath); err != nil {
  166. return
  167. }
  168. }()
  169. return ret, nil
  170. }
  171. func makeDictTypes(autoCode *system.AutoCodeStruct) {
  172. DictTypeM := make(map[string]string)
  173. for _, v := range autoCode.Fields {
  174. if v.DictType != "" {
  175. DictTypeM[v.DictType] = ""
  176. }
  177. }
  178. for k := range DictTypeM {
  179. autoCode.DictTypes = append(autoCode.DictTypes, k)
  180. }
  181. }
  182. // @author: [piexlmax](https://github.com/piexlmax)
  183. // @function: CreateTemp
  184. // @description: 创建代码
  185. // @param: model.AutoCodeStruct
  186. // @return: err error
  187. func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, menuID uint, ids ...uint) (err error) {
  188. fmtField(&autoCode)
  189. // 增加判断: 重复创建struct
  190. if autoCode.AutoMoveFile && AutoCodeHistoryServiceApp.Repeat(autoCode.BusinessDB, autoCode.StructName, autoCode.Package) {
  191. return RepeatErr
  192. }
  193. dataList, deptList, needMkdir, err := autoCodeService.getNeedList(&autoCode)
  194. if err != nil {
  195. return err
  196. }
  197. meta, _ := json.Marshal(autoCode)
  198. // 增加判断:Package不为空
  199. if autoCode.Package == "" {
  200. return errors.New("Package为空\n")
  201. }
  202. // 写入文件前,先创建文件夹
  203. if err = utils.CreateDir(needMkdir...); err != nil {
  204. return err
  205. }
  206. // 生成文件
  207. for _, value := range dataList {
  208. f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
  209. if err != nil {
  210. return err
  211. }
  212. if err = value.template.Execute(f, autoCode); err != nil {
  213. return err
  214. }
  215. _ = f.Close()
  216. }
  217. defer func() { // 移除中间文件
  218. if err := os.RemoveAll(autoPath); err != nil {
  219. return
  220. }
  221. }()
  222. bf := strings.Builder{}
  223. idBf := strings.Builder{}
  224. injectionCodeMeta := strings.Builder{}
  225. for _, id := range ids {
  226. idBf.WriteString(strconv.Itoa(int(id)))
  227. idBf.WriteString(";")
  228. }
  229. if autoCode.AutoMoveFile { // 判断是否需要自动转移
  230. Init(autoCode.Package)
  231. for index := range dataList {
  232. autoCodeService.addAutoMoveFile(&dataList[index])
  233. }
  234. // 判断目标文件是否都可以移动
  235. for _, value := range dataList {
  236. if utils.FileExist(value.autoMoveFilePath) {
  237. return errors.New(fmt.Sprintf("目标文件已存在:%s\n", value.autoMoveFilePath))
  238. }
  239. }
  240. for _, value := range dataList { // 移动文件
  241. if err := utils.FileMove(value.autoCodePath, value.autoMoveFilePath); err != nil {
  242. return err
  243. }
  244. }
  245. {
  246. // 在gorm.go 注入 自动迁移
  247. path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  248. global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go")
  249. varDB := utils.MaheHump(autoCode.BusinessDB)
  250. ast2.AddRegisterTablesAst(path, "RegisterTables", autoCode.Package, varDB, autoCode.BusinessDB, autoCode.StructName)
  251. }
  252. {
  253. // router.go 注入 自动迁移
  254. path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  255. global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go")
  256. ast2.AddRouterCode(path, "Routers", autoCode.Package, autoCode.StructName)
  257. }
  258. // 给各个enter进行注入
  259. err = injectionCode(autoCode.StructName, &injectionCodeMeta)
  260. if err != nil {
  261. return
  262. }
  263. // 保存生成信息
  264. for _, data := range dataList {
  265. if len(data.autoMoveFilePath) != 0 {
  266. bf.WriteString(data.autoMoveFilePath)
  267. bf.WriteString(";")
  268. }
  269. }
  270. } else { // 打包
  271. if err = utils.ZipFiles("./ginvueadmin.zip", deptList, ".", "."); err != nil {
  272. return err
  273. }
  274. }
  275. if autoCode.AutoMoveFile || autoCode.AutoCreateApiToSql || autoCode.AutoCreateMenuToSql {
  276. if autoCode.TableName != "" {
  277. err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
  278. string(meta),
  279. autoCode.StructName,
  280. autoCode.Description,
  281. bf.String(),
  282. injectionCodeMeta.String(),
  283. autoCode.TableName,
  284. idBf.String(),
  285. autoCode.Package,
  286. autoCode.BusinessDB,
  287. menuID,
  288. )
  289. } else {
  290. err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
  291. string(meta),
  292. autoCode.StructName,
  293. autoCode.Description,
  294. bf.String(),
  295. injectionCodeMeta.String(),
  296. autoCode.StructName,
  297. idBf.String(),
  298. autoCode.Package,
  299. autoCode.BusinessDB,
  300. menuID,
  301. )
  302. }
  303. }
  304. if err != nil {
  305. return err
  306. }
  307. if autoCode.AutoMoveFile {
  308. return system.ErrAutoMove
  309. }
  310. return nil
  311. }
  312. // @author: [piexlmax](https://github.com/piexlmax)
  313. // @function: GetAllTplFile
  314. // @description: 获取 pathName 文件夹下所有 tpl 文件
  315. // @param: pathName string, deptList []string
  316. // @return: []string, error
  317. func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, deptList []string) ([]string, error) {
  318. depts, err := os.ReadDir(pathName)
  319. for _, fi := range depts {
  320. if fi.IsDir() {
  321. deptList, err = autoCodeService.GetAllTplFile(pathName+"/"+fi.Name(), deptList)
  322. if err != nil {
  323. return nil, err
  324. }
  325. } else {
  326. if strings.HasSuffix(fi.Name(), ".tpl") {
  327. deptList = append(deptList, pathName+"/"+fi.Name())
  328. }
  329. }
  330. }
  331. return deptList, err
  332. }
  333. // @author: [piexlmax](https://github.com/piexlmax)
  334. // @function: GetDB
  335. // @description: 获取指定数据库和指定数据表的所有字段名,类型值等
  336. // @param: tableName string, dbName string
  337. // @return: err error, Columns []request.ColumnReq
  338. func (autoCodeService *AutoCodeService) DropTable(BusinessDb, tableName string) error {
  339. if BusinessDb != "" {
  340. return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error
  341. } else {
  342. return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
  343. }
  344. }
  345. // @author: [SliverHorn](https://github.com/SliverHorn)
  346. // @author: [songzhibin97](https://github.com/songzhibin97)
  347. // @function: addAutoMoveFile
  348. // @description: 生成对应的迁移文件路径
  349. // @param: *tplData
  350. // @return: null
  351. func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
  352. base := filepath.Base(data.autoCodePath)
  353. deptSlice := strings.Split(data.autoCodePath, string(os.PathSeparator))
  354. n := len(deptSlice)
  355. if n <= 2 {
  356. return
  357. }
  358. if strings.Contains(deptSlice[1], "server") {
  359. if strings.Contains(deptSlice[n-2], "router") {
  360. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server,
  361. fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, data.autoPackage), base)
  362. } else if strings.Contains(deptSlice[n-2], "api") {
  363. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  364. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, data.autoPackage), base)
  365. } else if strings.Contains(deptSlice[n-2], "service") {
  366. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  367. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, data.autoPackage), base)
  368. } else if strings.Contains(deptSlice[n-2], "model") {
  369. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  370. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SModel, data.autoPackage), base)
  371. } else if strings.Contains(deptSlice[n-2], "request") {
  372. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  373. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRequest, data.autoPackage), base)
  374. }
  375. } else if strings.Contains(deptSlice[1], "web") {
  376. if strings.Contains(deptSlice[n-1], "js") {
  377. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  378. global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi, data.autoPackage, base)
  379. } else if strings.Contains(deptSlice[n-2], "form") {
  380. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  381. global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm, data.autoPackage, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), strings.TrimSuffix(base, filepath.Ext(base))+"Form.vue")
  382. } else if strings.Contains(deptSlice[n-2], "table") {
  383. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  384. global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WTable, data.autoPackage, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), base)
  385. }
  386. }
  387. }
  388. // @author: [piexlmax](https://github.com/piexlmax)
  389. // @author: [SliverHorn](https://github.com/SliverHorn)
  390. // @function: CreateApi
  391. // @description: 自动创建api数据,
  392. // @param: a *model.AutoCodeStruct
  393. // @return: err error
  394. func (autoCodeService *AutoCodeService) AutoCreateApi(a *system.AutoCodeStruct) (ids []uint, err error) {
  395. apiList := []system.SysApi{
  396. {
  397. Path: "/" + a.Abbreviation + "/" + "create" + a.StructName,
  398. Description: "新增" + a.Description,
  399. ApiGroup: a.Description,
  400. Method: "POST",
  401. },
  402. {
  403. Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName,
  404. Description: "删除" + a.Description,
  405. ApiGroup: a.Description,
  406. Method: "DELETE",
  407. },
  408. {
  409. Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName + "ByIds",
  410. Description: "批量删除" + a.Description,
  411. ApiGroup: a.Description,
  412. Method: "DELETE",
  413. },
  414. {
  415. Path: "/" + a.Abbreviation + "/" + "update" + a.StructName,
  416. Description: "更新" + a.Description,
  417. ApiGroup: a.Description,
  418. Method: "PUT",
  419. },
  420. {
  421. Path: "/" + a.Abbreviation + "/" + "find" + a.StructName,
  422. Description: "根据ID获取" + a.Description,
  423. ApiGroup: a.Description,
  424. Method: "GET",
  425. },
  426. {
  427. Path: "/" + a.Abbreviation + "/" + "get" + a.StructName + "List",
  428. Description: "获取" + a.Description + "列表",
  429. ApiGroup: a.Description,
  430. Method: "GET",
  431. },
  432. }
  433. err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
  434. for _, v := range apiList {
  435. var api system.SysApi
  436. if errors.Is(tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error, gorm.ErrRecordNotFound) {
  437. if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
  438. return err
  439. } else {
  440. ids = append(ids, v.ID)
  441. }
  442. }
  443. }
  444. return nil
  445. })
  446. return ids, err
  447. }
  448. func (autoCodeService *AutoCodeService) AutoCreateMenu(a *system.AutoCodeStruct) (id uint, err error) {
  449. var menu system.SysBaseMenu
  450. err = global.GVA_DB.First(&menu, "name = ?", a.Abbreviation).Error
  451. if err == nil {
  452. return 0, errors.New("存在相同的菜单路由,请关闭自动创建菜单功能")
  453. }
  454. menu.ParentId = 0
  455. menu.Name = a.Abbreviation
  456. menu.Path = a.Abbreviation
  457. menu.Meta.Title = a.Description
  458. menu.Component = fmt.Sprintf("view/%s/%s/%s.vue", a.Package, a.PackageName, a.PackageName)
  459. err = global.GVA_DB.Create(&menu).Error
  460. return menu.ID, err
  461. }
  462. func (autoCodeService *AutoCodeService) getNeedList(autoCode *system.AutoCodeStruct) (dataList []tplData, deptList []string, needMkdir []string, err error) {
  463. // 去除所有空格
  464. utils.TrimSpace(autoCode)
  465. for _, field := range autoCode.Fields {
  466. utils.TrimSpace(field)
  467. }
  468. // 获取 basePath 文件夹下所有tpl文件
  469. tplFileList, err := autoCodeService.GetAllTplFile(autocodePath, nil)
  470. if err != nil {
  471. return nil, nil, nil, err
  472. }
  473. dataList = make([]tplData, 0, len(tplFileList))
  474. deptList = make([]string, 0, len(tplFileList))
  475. needMkdir = make([]string, 0, len(tplFileList)) // 当文件夹下存在多个tpl文件时,改为map更合理
  476. // 根据文件路径生成 tplData 结构体,待填充数据
  477. for _, value := range tplFileList {
  478. dataList = append(dataList, tplData{locationPath: value, autoPackage: autoCode.Package})
  479. }
  480. // 生成 *Template, 填充 template 字段
  481. for index, value := range dataList {
  482. dataList[index].template, err = template.ParseFiles(value.locationPath)
  483. if err != nil {
  484. return nil, nil, nil, err
  485. }
  486. }
  487. // 生成文件路径,填充 autoCodePath 字段,readme.txt.tpl不符合规则,需要特殊处理
  488. // resource/template/web/api.js.tpl -> autoCode/web/autoCode.PackageName/api/autoCode.PackageName.js
  489. // resource/template/readme.txt.tpl -> autoCode/readme.txt
  490. for index, value := range dataList {
  491. trimBase := strings.TrimPrefix(value.locationPath, autocodePath+"/")
  492. if trimBase == "readme.txt.tpl" {
  493. dataList[index].autoCodePath = autoPath + "readme.txt"
  494. continue
  495. }
  496. if lastSeparator := strings.LastIndex(trimBase, "/"); lastSeparator != -1 {
  497. origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
  498. firstDot := strings.Index(origFileName, ".")
  499. if firstDot != -1 {
  500. var deptName string
  501. if origFileName[firstDot:] != ".go" {
  502. deptName = autoCode.PackageName + origFileName[firstDot:]
  503. } else {
  504. deptName = autoCode.HumpPackageName + origFileName[firstDot:]
  505. }
  506. dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
  507. origFileName[:firstDot], deptName)
  508. }
  509. }
  510. if lastSeparator := strings.LastIndex(dataList[index].autoCodePath, string(os.PathSeparator)); lastSeparator != -1 {
  511. needMkdir = append(needMkdir, dataList[index].autoCodePath[:lastSeparator])
  512. }
  513. }
  514. for _, value := range dataList {
  515. deptList = append(deptList, value.autoCodePath)
  516. }
  517. return dataList, deptList, needMkdir, err
  518. }
  519. // injectionCode 封装代码注入
  520. func injectionCode(structName string, bf *strings.Builder) error {
  521. for _, meta := range injectionPaths {
  522. code := fmt.Sprintf(meta.structNameF, structName)
  523. ast2.ImportForAutoEnter(meta.path, meta.funcName, code)
  524. bf.WriteString(fmt.Sprintf("%s@%s@%s;", meta.path, meta.funcName, code))
  525. }
  526. return nil
  527. }
  528. func (autoCodeService *AutoCodeService) CreateAutoCode(s *system.SysAutoCode) error {
  529. if s.PackageName == "autocode" || s.PackageName == "system" || s.PackageName == "example" || s.PackageName == "" {
  530. return errors.New("不能使用已保留的package name")
  531. }
  532. if !errors.Is(global.GVA_DB.Where("package_name = ?", s.PackageName).First(&system.SysAutoCode{}).Error, gorm.ErrRecordNotFound) {
  533. return errors.New("存在相同PackageName")
  534. }
  535. if e := autoCodeService.CreatePackageTemp(s.PackageName); e != nil {
  536. return e
  537. }
  538. return global.GVA_DB.Create(&s).Error
  539. }
  540. func (autoCodeService *AutoCodeService) GetPackage() (pkgList []system.SysAutoCode, err error) {
  541. err = global.GVA_DB.Find(&pkgList).Error
  542. return pkgList, err
  543. }
  544. func (autoCodeService *AutoCodeService) DelPackage(a system.SysAutoCode) error {
  545. return global.GVA_DB.Delete(&a).Error
  546. }
  547. func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) error {
  548. Init(packageName)
  549. pendingTemp := []autoPackage{{
  550. path: packageService,
  551. name: packageServiceName,
  552. temp: string(subcontract.Server),
  553. }, {
  554. path: packageRouter,
  555. name: packageRouterName,
  556. temp: string(subcontract.Router),
  557. }, {
  558. path: packageAPI,
  559. name: packageAPIName,
  560. temp: string(subcontract.API),
  561. }}
  562. webTemp := []string{
  563. filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi),
  564. filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm),
  565. }
  566. for _, s := range webTemp {
  567. err := os.MkdirAll(filepath.Join(s, packageName), 0755)
  568. if err != nil {
  569. return err
  570. }
  571. }
  572. for i, s := range pendingTemp {
  573. pendingTemp[i].path = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, filepath.Clean(fmt.Sprintf(s.path, packageName)))
  574. }
  575. // 选择模板
  576. for _, s := range pendingTemp {
  577. err := os.MkdirAll(filepath.Dir(s.path), 0755)
  578. if err != nil {
  579. return err
  580. }
  581. f, err := os.Create(s.path)
  582. if err != nil {
  583. return err
  584. }
  585. defer f.Close()
  586. temp, err := template.New("").Parse(s.temp)
  587. if err != nil {
  588. return err
  589. }
  590. err = temp.Execute(f, struct {
  591. PackageName string `json:"package_name"`
  592. }{packageName})
  593. if err != nil {
  594. return err
  595. }
  596. }
  597. // 创建完成后在对应的位置插入结构代码
  598. for _, v := range pendingTemp {
  599. meta := packageInjectionMap[v.name]
  600. if err := ast2.ImportReference(meta.path, fmt.Sprintf(meta.importCodeF, v.name, packageName), fmt.Sprintf(meta.structNameF, utils.FirstUpper(packageName)), fmt.Sprintf(meta.packageNameF, packageName), meta.groupName); err != nil {
  601. return err
  602. }
  603. }
  604. return nil
  605. }
  606. // CreatePlug 自动创建插件模板
  607. func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) error {
  608. // 检查列表参数是否有效
  609. plug.CheckList()
  610. err := autoCodeService.createPluginServer(plug)
  611. if err != nil {
  612. return err
  613. }
  614. err = autoCodeService.createPluginWeb(plug)
  615. if err != nil {
  616. return err
  617. }
  618. return nil
  619. }
  620. func (autoCodeService *AutoCodeService) createPluginServer(plug system.AutoPlugReq) error {
  621. tplFileList, _ := autoCodeService.GetAllTplFile(plugServerPath, nil)
  622. for _, tpl := range tplFileList {
  623. temp, err := template.ParseFiles(tpl)
  624. if err != nil {
  625. zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
  626. return err
  627. }
  628. pathArr := strings.SplitAfter(tpl, "/")
  629. if strings.Index(pathArr[3], "tpl") < 0 {
  630. dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[3]))
  631. os.MkdirAll(dirPath, 0755)
  632. }
  633. dept := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugServerPath):len(tpl)-4]))
  634. f, err := os.OpenFile(dept, os.O_WRONLY|os.O_CREATE, 0666)
  635. if err != nil {
  636. zap.L().Error("open dept", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
  637. return err
  638. }
  639. defer f.Close()
  640. err = temp.Execute(f, plug)
  641. if err != nil {
  642. zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
  643. return err
  644. }
  645. }
  646. return nil
  647. }
  648. func (autoCodeService *AutoCodeService) createPluginWeb(plug system.AutoPlugReq) error {
  649. tplFileList, _ := autoCodeService.GetAllTplFile(plugWebPath, nil)
  650. for _, tpl := range tplFileList {
  651. temp, err := template.ParseFiles(tpl)
  652. if err != nil {
  653. zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
  654. return err
  655. }
  656. pathArr := strings.SplitAfter(tpl, "/")
  657. if strings.Index(pathArr[3], "tpl") < 0 {
  658. dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[3]))
  659. os.MkdirAll(dirPath, 0755)
  660. }
  661. dept := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugWebPath):len(tpl)-4]))
  662. f, err := os.OpenFile(dept, os.O_WRONLY|os.O_CREATE, 0666)
  663. if err != nil {
  664. zap.L().Error("open dept", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
  665. return err
  666. }
  667. defer f.Close()
  668. err = temp.Execute(f, plug)
  669. if err != nil {
  670. zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
  671. return err
  672. }
  673. }
  674. return nil
  675. }
  676. func (autoCodeService *AutoCodeService) InstallPlugin(dept *multipart.FileHeader) (web, server int, err error) {
  677. const GVAPLUGPINATH = "./gva-plug-temp/"
  678. defer os.RemoveAll(GVAPLUGPINATH)
  679. _, err = os.Stat(GVAPLUGPINATH)
  680. if os.IsNotExist(err) {
  681. os.Mkdir(GVAPLUGPINATH, os.ModePerm)
  682. }
  683. src, err := dept.Open()
  684. if err != nil {
  685. return -1, -1, err
  686. }
  687. defer src.Close()
  688. out, err := os.Create(GVAPLUGPINATH + dept.Filename)
  689. if err != nil {
  690. return -1, -1, err
  691. }
  692. defer out.Close()
  693. _, err = io.Copy(out, src)
  694. paths, err := utils.Unzip(GVAPLUGPINATH+dept.Filename, GVAPLUGPINATH)
  695. paths = filterFile(paths)
  696. var webIndex = -1
  697. var serverIndex = -1
  698. webPlugin := ""
  699. serverPlugin := ""
  700. for i := range paths {
  701. paths[i] = filepath.ToSlash(paths[i])
  702. pathArr := strings.Split(paths[i], "/")
  703. ln := len(pathArr)
  704. if ln < 4 {
  705. continue
  706. }
  707. if pathArr[2]+"/"+pathArr[3] == `server/plugin` && len(serverPlugin) == 0 {
  708. serverPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
  709. }
  710. if pathArr[2]+"/"+pathArr[3] == `web/plugin` && len(webPlugin) == 0 {
  711. webPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
  712. }
  713. }
  714. if len(serverPlugin) == 0 && len(webPlugin) == 0 {
  715. zap.L().Error("非标准插件,请按照文档自动迁移使用")
  716. return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用")
  717. }
  718. if len(serverPlugin) != 0 {
  719. err = installation(serverPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server)
  720. if err != nil {
  721. return webIndex, serverIndex, err
  722. }
  723. }
  724. if len(webPlugin) != 0 {
  725. err = installation(webPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web)
  726. if err != nil {
  727. return webIndex, serverIndex, err
  728. }
  729. }
  730. return 1, 1, err
  731. }
  732. func installation(path string, formPath string, toPath string) error {
  733. arr := strings.Split(filepath.ToSlash(path), "/")
  734. ln := len(arr)
  735. if ln < 3 {
  736. return errors.New("arr")
  737. }
  738. name := arr[ln-3]
  739. var form = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + formPath + "/" + path)
  740. var to = filepath.ToSlash(global.GVA_CONFIG.AutoCode.Root + toPath + "/plugin/")
  741. _, err := os.Stat(to + name)
  742. if err == nil {
  743. zap.L().Error("autoPath 已存在同名插件,请自行手动安装", zap.String("to", to))
  744. return errors.New(toPath + "已存在同名插件,请自行手动安装")
  745. }
  746. return cp.Copy(form, to, cp.Options{Skip: skipMacSpecialDocument})
  747. }
  748. func filterFile(paths []string) []string {
  749. np := make([]string, 0, len(paths))
  750. for _, path := range paths {
  751. if ok, _ := skipMacSpecialDocument(path); ok {
  752. continue
  753. }
  754. np = append(np, path)
  755. }
  756. return np
  757. }
  758. func skipMacSpecialDocument(src string) (bool, error) {
  759. if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") {
  760. return true, nil
  761. }
  762. return false, nil
  763. }
  764. func (autoCodeService *AutoCodeService) PubPlug(plugName string) (zipPath string, err error) {
  765. if plugName == "" {
  766. return "", errors.New("插件名称不能为空")
  767. }
  768. // 防止路径穿越
  769. plugName = filepath.Clean(plugName)
  770. webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName)
  771. serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName)
  772. // 创建一个新的zip文件
  773. // 判断目录是否存在
  774. _, err = os.Stat(webPath)
  775. if err != nil {
  776. return "", errors.New("web路径不存在")
  777. }
  778. _, err = os.Stat(serverPath)
  779. if err != nil {
  780. return "", errors.New("server路径不存在")
  781. }
  782. deptName := plugName + ".zip"
  783. // 创建一个新的zip文件
  784. zipFile, err := os.Create(deptName)
  785. if err != nil {
  786. fmt.Println(err)
  787. return
  788. }
  789. defer zipFile.Close()
  790. // 创建一个zip写入器
  791. zipWriter := zip.NewWriter(zipFile)
  792. defer zipWriter.Close()
  793. webHeaderName := filepath.Join(plugName, "web", "plugin", plugName)
  794. err = autoCodeService.doZip(zipWriter, webPath, webHeaderName)
  795. if err != nil {
  796. return
  797. }
  798. serverHeaderName := filepath.Join(plugName, "server", "plugin", plugName)
  799. err = autoCodeService.doZip(zipWriter, serverPath, serverHeaderName)
  800. if err != nil {
  801. return
  802. }
  803. return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, deptName), nil
  804. }
  805. /*
  806. *
  807. zipWriter zip写入器
  808. serverPath 存储的路径
  809. headerName 写有zip的路径
  810. *
  811. */
  812. func (autoCodeService *AutoCodeService) doZip(zipWriter *zip.Writer, serverPath, headerName string) (err error) {
  813. // 遍历serverPath目录并将所有非隐藏文件添加到zip归档中
  814. err = filepath.Walk(serverPath, func(path string, info os.FileInfo, err error) error {
  815. if err != nil {
  816. return err
  817. }
  818. // 跳过隐藏文件和目录
  819. if strings.HasPrefix(info.Name(), ".") {
  820. return nil
  821. }
  822. // 创建一个新的文件头
  823. header, err := zip.FileInfoHeader(info)
  824. if err != nil {
  825. return err
  826. }
  827. // 将文件头的名称设置为文件的相对路径
  828. rel, _ := filepath.Rel(serverPath, path)
  829. header.Name = filepath.Join(headerName, rel)
  830. // 目录需要拼上一个 "/" ,否则会出现一个和目录一样的文件在压缩包中
  831. if info.IsDir() {
  832. header.Name += "/"
  833. }
  834. // 将文件添加到zip归档中
  835. writer, err := zipWriter.CreateHeader(header)
  836. if err != nil {
  837. return err
  838. }
  839. if info.IsDir() {
  840. return nil
  841. }
  842. // 打开文件并将其内容复制到zip归档中
  843. dept, err := os.Open(path)
  844. if err != nil {
  845. return err
  846. }
  847. defer dept.Close()
  848. _, err = io.Copy(writer, dept)
  849. if err != nil {
  850. return err
  851. }
  852. return nil
  853. })
  854. return err
  855. }
  856. func fmtField(autoCode *system.AutoCodeStruct) {
  857. makeDictTypes(autoCode)
  858. autoCode.DataSourceMap = make(map[string]*system.DataSource)
  859. for i := range autoCode.Fields {
  860. if autoCode.Fields[i].Front {
  861. autoCode.FrontFields = append(autoCode.FrontFields, autoCode.Fields[i])
  862. }
  863. if autoCode.Fields[i].FieldType == "time.Time" {
  864. autoCode.HasTimer = true
  865. if autoCode.Fields[i].FieldSearchType != "" {
  866. autoCode.HasSearchTimer = true
  867. }
  868. }
  869. if autoCode.Fields[i].Sort {
  870. autoCode.NeedSort = true
  871. }
  872. if autoCode.Fields[i].FieldType == "picture" {
  873. autoCode.HasPic = true
  874. }
  875. if autoCode.Fields[i].FieldType == "video" {
  876. autoCode.HasPic = true
  877. }
  878. if autoCode.Fields[i].FieldType == "richtext" {
  879. autoCode.HasRichText = true
  880. }
  881. if autoCode.Fields[i].FieldType == "pictures" {
  882. autoCode.HasPic = true
  883. autoCode.NeedJSON = true
  884. }
  885. if autoCode.Fields[i].FieldType == "json" {
  886. autoCode.NeedJSON = true
  887. }
  888. if autoCode.Fields[i].FieldType == "dept" {
  889. autoCode.HasFile = true
  890. autoCode.NeedJSON = true
  891. }
  892. if autoCode.Fields[i].DataSource != nil &&
  893. autoCode.Fields[i].DataSource.Table != "" &&
  894. autoCode.Fields[i].DataSource.Label != "" &&
  895. autoCode.Fields[i].DataSource.Value != "" {
  896. autoCode.HasDataSource = true
  897. autoCode.DataSourceMap[autoCode.Fields[i].FieldJson] = autoCode.Fields[i].DataSource
  898. autoCode.Fields[i].CheckDataSource = true
  899. }
  900. if autoCode.GvaModel {
  901. autoCode.PrimaryField = &system.Field{
  902. FieldName: "ID",
  903. FieldType: "uint",
  904. FieldDesc: "ID",
  905. FieldJson: "ID",
  906. DataTypeLong: "20",
  907. Comment: "主键ID",
  908. ColumnName: "id",
  909. }
  910. }
  911. if !autoCode.GvaModel && autoCode.PrimaryField == nil && autoCode.Fields[i].PrimaryKey {
  912. autoCode.PrimaryField = autoCode.Fields[i]
  913. }
  914. }
  915. }