sys_auto_code.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. package system
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "go.uber.org/zap"
  8. "go/ast"
  9. "go/format"
  10. "go/parser"
  11. "go/token"
  12. "golang.org/x/text/cases"
  13. "golang.org/x/text/language"
  14. "io/ioutil"
  15. "log"
  16. "os"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "text/template"
  21. "log-server/resource/autocode_template/subcontract"
  22. "log-server/global"
  23. "log-server/model/system"
  24. "log-server/utils"
  25. "gorm.io/gorm"
  26. )
  27. const (
  28. autoPath = "autocode_template/"
  29. autocodePath = "resource/autocode_template"
  30. plugPath = "resource/plug_template"
  31. packageService = "service/%s/enter.go"
  32. packageServiceName = "service"
  33. packageRouter = "router/%s/enter.go"
  34. packageRouterName = "router"
  35. packageAPI = "api/v1/%s/enter.go"
  36. packageAPIName = "api/v1"
  37. )
  38. type autoPackage struct {
  39. path string
  40. temp string
  41. name string
  42. }
  43. var (
  44. packageInjectionMap map[string]astInjectionMeta
  45. injectionPaths []injectionMeta
  46. caser = cases.Title(language.English)
  47. )
  48. func Init(Package string) {
  49. injectionPaths = []injectionMeta{
  50. {
  51. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  52. global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go"),
  53. funcName: "MysqlTables",
  54. structNameF: Package + ".%s{},",
  55. },
  56. {
  57. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  58. global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go"),
  59. funcName: "Routers",
  60. structNameF: Package + "Router.Init%sRouter(PrivateGroup)",
  61. },
  62. {
  63. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  64. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, Package), "enter.go"),
  65. funcName: "ApiGroup",
  66. structNameF: "%sApi",
  67. },
  68. {
  69. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  70. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, Package), "enter.go"),
  71. funcName: "RouterGroup",
  72. structNameF: "%sRouter",
  73. },
  74. {
  75. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  76. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, Package), "enter.go"),
  77. funcName: "ServiceGroup",
  78. structNameF: "%sService",
  79. },
  80. }
  81. packageInjectionMap = map[string]astInjectionMeta{
  82. packageServiceName: {
  83. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  84. global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"),
  85. importCodeF: "log-server/%s/%s",
  86. packageNameF: "%s",
  87. groupName: "ServiceGroup",
  88. structNameF: "%sServiceGroup",
  89. },
  90. packageRouterName: {
  91. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  92. global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"),
  93. importCodeF: "log-server/%s/%s",
  94. packageNameF: "%s",
  95. groupName: "RouterGroup",
  96. structNameF: "%s",
  97. },
  98. packageAPIName: {
  99. path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  100. global.GVA_CONFIG.AutoCode.Server, "api/v1", "enter.go"),
  101. importCodeF: "log-server/%s/%s",
  102. packageNameF: "%s",
  103. groupName: "ApiGroup",
  104. structNameF: "%sApiGroup",
  105. },
  106. }
  107. }
  108. type injectionMeta struct {
  109. path string
  110. funcName string
  111. structNameF string // 带格式化的
  112. }
  113. type astInjectionMeta struct {
  114. path string
  115. importCodeF string
  116. structNameF string
  117. packageNameF string
  118. groupName string
  119. }
  120. type tplData struct {
  121. template *template.Template
  122. autoPackage string
  123. locationPath string
  124. autoCodePath string
  125. autoMoveFilePath string
  126. }
  127. type AutoCodeService struct{}
  128. var AutoCodeServiceApp = new(AutoCodeService)
  129. //@author: [songzhibin97](https://github.com/songzhibin97)
  130. //@function: PreviewTemp
  131. //@description: 预览创建代码
  132. //@param: model.AutoCodeStruct
  133. //@return: map[string]string, error
  134. func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStruct) (map[string]string, error) {
  135. makeDictTypes(&autoCode)
  136. for i := range autoCode.Fields {
  137. if autoCode.Fields[i].FieldType == "time.Time" {
  138. autoCode.HasTimer = true
  139. break
  140. }
  141. }
  142. dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
  143. if err != nil {
  144. return nil, err
  145. }
  146. // 写入文件前,先创建文件夹
  147. if err = utils.CreateDir(needMkdir...); err != nil {
  148. return nil, err
  149. }
  150. // 创建map
  151. ret := make(map[string]string)
  152. // 生成map
  153. for _, value := range dataList {
  154. ext := ""
  155. if ext = filepath.Ext(value.autoCodePath); ext == ".txt" {
  156. continue
  157. }
  158. f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
  159. if err != nil {
  160. return nil, err
  161. }
  162. if err = value.template.Execute(f, autoCode); err != nil {
  163. return nil, err
  164. }
  165. _ = f.Close()
  166. f, err = os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_RDONLY, 0o755)
  167. if err != nil {
  168. return nil, err
  169. }
  170. builder := strings.Builder{}
  171. builder.WriteString("```")
  172. if ext != "" && strings.Contains(ext, ".") {
  173. builder.WriteString(strings.Replace(ext, ".", "", -1))
  174. }
  175. builder.WriteString("\n\n")
  176. data, err := ioutil.ReadAll(f)
  177. if err != nil {
  178. return nil, err
  179. }
  180. builder.Write(data)
  181. builder.WriteString("\n\n```")
  182. pathArr := strings.Split(value.autoCodePath, string(os.PathSeparator))
  183. ret[pathArr[1]+"-"+pathArr[3]] = builder.String()
  184. _ = f.Close()
  185. }
  186. defer func() { // 移除中间文件
  187. if err := os.RemoveAll(autoPath); err != nil {
  188. return
  189. }
  190. }()
  191. return ret, nil
  192. }
  193. func makeDictTypes(autoCode *system.AutoCodeStruct) {
  194. DictTypeM := make(map[string]string)
  195. for _, v := range autoCode.Fields {
  196. if v.DictType != "" {
  197. DictTypeM[v.DictType] = ""
  198. }
  199. }
  200. for k := range DictTypeM {
  201. autoCode.DictTypes = append(autoCode.DictTypes, k)
  202. }
  203. }
  204. //@author: [piexlmax](https://github.com/piexlmax)
  205. //@function: CreateTemp
  206. //@description: 创建代码
  207. //@param: model.AutoCodeStruct
  208. //@return: err error
  209. func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, ids ...uint) (err error) {
  210. makeDictTypes(&autoCode)
  211. for i := range autoCode.Fields {
  212. if autoCode.Fields[i].FieldType == "time.Time" {
  213. autoCode.HasTimer = true
  214. break
  215. }
  216. }
  217. // 增加判断: 重复创建struct
  218. if autoCode.AutoMoveFile && AutoCodeHistoryServiceApp.Repeat(autoCode.StructName, autoCode.Package) {
  219. return RepeatErr
  220. }
  221. dataList, fileList, needMkdir, err := autoCodeService.getNeedList(&autoCode)
  222. if err != nil {
  223. return err
  224. }
  225. meta, _ := json.Marshal(autoCode)
  226. // 写入文件前,先创建文件夹
  227. if err = utils.CreateDir(needMkdir...); err != nil {
  228. return err
  229. }
  230. // 生成文件
  231. for _, value := range dataList {
  232. f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
  233. if err != nil {
  234. return err
  235. }
  236. if err = value.template.Execute(f, autoCode); err != nil {
  237. return err
  238. }
  239. _ = f.Close()
  240. }
  241. defer func() { // 移除中间文件
  242. if err := os.RemoveAll(autoPath); err != nil {
  243. return
  244. }
  245. }()
  246. bf := strings.Builder{}
  247. idBf := strings.Builder{}
  248. injectionCodeMeta := strings.Builder{}
  249. for _, id := range ids {
  250. idBf.WriteString(strconv.Itoa(int(id)))
  251. idBf.WriteString(";")
  252. }
  253. if autoCode.AutoMoveFile { // 判断是否需要自动转移
  254. Init(autoCode.Package)
  255. for index := range dataList {
  256. autoCodeService.addAutoMoveFile(&dataList[index])
  257. }
  258. // 判断目标文件是否都可以移动
  259. for _, value := range dataList {
  260. if utils.FileExist(value.autoMoveFilePath) {
  261. return errors.New(fmt.Sprintf("目标文件已存在:%s\n", value.autoMoveFilePath))
  262. }
  263. }
  264. for _, value := range dataList { // 移动文件
  265. if err := utils.FileMove(value.autoCodePath, value.autoMoveFilePath); err != nil {
  266. return err
  267. }
  268. }
  269. err = injectionCode(autoCode.StructName, &injectionCodeMeta)
  270. if err != nil {
  271. return
  272. }
  273. // 保存生成信息
  274. for _, data := range dataList {
  275. if len(data.autoMoveFilePath) != 0 {
  276. bf.WriteString(data.autoMoveFilePath)
  277. bf.WriteString(";")
  278. }
  279. }
  280. var gormPath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  281. global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go")
  282. var routePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  283. global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go")
  284. var imporStr = fmt.Sprintf("log-server/model/%s", autoCode.Package)
  285. _ = ImportReference(routePath, "", "", autoCode.Package, "")
  286. _ = ImportReference(gormPath, imporStr, "", "", "")
  287. } else { // 打包
  288. if err = utils.ZipFiles("./ginvueadmin.zip", fileList, ".", "."); err != nil {
  289. return err
  290. }
  291. }
  292. if autoCode.AutoMoveFile || autoCode.AutoCreateApiToSql {
  293. if autoCode.TableName != "" {
  294. err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
  295. string(meta),
  296. autoCode.StructName,
  297. autoCode.Description,
  298. bf.String(),
  299. injectionCodeMeta.String(),
  300. autoCode.TableName,
  301. idBf.String(),
  302. autoCode.Package,
  303. )
  304. } else {
  305. err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
  306. string(meta),
  307. autoCode.StructName,
  308. autoCode.Description,
  309. bf.String(),
  310. injectionCodeMeta.String(),
  311. autoCode.StructName,
  312. idBf.String(),
  313. autoCode.Package,
  314. )
  315. }
  316. }
  317. if err != nil {
  318. return err
  319. }
  320. if autoCode.AutoMoveFile {
  321. return system.AutoMoveErr
  322. }
  323. return nil
  324. }
  325. //@author: [piexlmax](https://github.com/piexlmax)
  326. //@function: GetAllTplFile
  327. //@description: 获取 pathName 文件夹下所有 tpl 文件
  328. //@param: pathName string, fileList []string
  329. //@return: []string, error
  330. func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, fileList []string) ([]string, error) {
  331. files, err := ioutil.ReadDir(pathName)
  332. for _, fi := range files {
  333. if fi.IsDir() {
  334. fileList, err = autoCodeService.GetAllTplFile(pathName+"/"+fi.Name(), fileList)
  335. if err != nil {
  336. return nil, err
  337. }
  338. } else {
  339. if strings.HasSuffix(fi.Name(), ".tpl") {
  340. fileList = append(fileList, pathName+"/"+fi.Name())
  341. }
  342. }
  343. }
  344. return fileList, err
  345. }
  346. //@author: [piexlmax](https://github.com/piexlmax)
  347. //@function: GetDB
  348. //@description: 获取指定数据库和指定数据表的所有字段名,类型值等
  349. //@param: tableName string, dbName string
  350. //@return: err error, Columns []request.ColumnReq
  351. func (autoCodeService *AutoCodeService) DropTable(tableName string) error {
  352. return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
  353. }
  354. //@author: [SliverHorn](https://github.com/SliverHorn)
  355. //@author: [songzhibin97](https://github.com/songzhibin97)
  356. //@function: addAutoMoveFile
  357. //@description: 生成对应的迁移文件路径
  358. //@param: *tplData
  359. //@return: null
  360. func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
  361. base := filepath.Base(data.autoCodePath)
  362. fileSlice := strings.Split(data.autoCodePath, string(os.PathSeparator))
  363. n := len(fileSlice)
  364. if n <= 2 {
  365. return
  366. }
  367. if strings.Contains(fileSlice[1], "server") {
  368. if strings.Contains(fileSlice[n-2], "router") {
  369. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server,
  370. fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, data.autoPackage), base)
  371. } else if strings.Contains(fileSlice[n-2], "api") {
  372. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  373. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, data.autoPackage), base)
  374. } else if strings.Contains(fileSlice[n-2], "service") {
  375. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  376. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, data.autoPackage), base)
  377. } else if strings.Contains(fileSlice[n-2], "model") {
  378. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  379. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SModel, data.autoPackage), base)
  380. } else if strings.Contains(fileSlice[n-2], "request") {
  381. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  382. global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRequest, data.autoPackage), base)
  383. }
  384. } else if strings.Contains(fileSlice[1], "web") {
  385. if strings.Contains(fileSlice[n-1], "js") {
  386. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  387. global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi, base)
  388. } else if strings.Contains(fileSlice[n-2], "form") {
  389. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  390. global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), strings.TrimSuffix(base, filepath.Ext(base))+"Form.vue")
  391. } else if strings.Contains(fileSlice[n-2], "table") {
  392. data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
  393. global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WTable, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), base)
  394. }
  395. }
  396. }
  397. //@author: [piexlmax](https://github.com/piexlmax)
  398. //@author: [SliverHorn](https://github.com/SliverHorn)
  399. //@function: CreateApi
  400. //@description: 自动创建api数据,
  401. //@param: a *model.AutoCodeStruct
  402. //@return: err error
  403. func (autoCodeService *AutoCodeService) AutoCreateApi(a *system.AutoCodeStruct) (ids []uint, err error) {
  404. apiList := []system.SysApi{
  405. {
  406. Path: "/" + a.Abbreviation + "/" + "create" + a.StructName,
  407. Description: "新增" + a.Description,
  408. ApiGroup: a.Abbreviation,
  409. Method: "POST",
  410. },
  411. {
  412. Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName,
  413. Description: "删除" + a.Description,
  414. ApiGroup: a.Abbreviation,
  415. Method: "DELETE",
  416. },
  417. {
  418. Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName + "ByIds",
  419. Description: "批量删除" + a.Description,
  420. ApiGroup: a.Abbreviation,
  421. Method: "DELETE",
  422. },
  423. {
  424. Path: "/" + a.Abbreviation + "/" + "update" + a.StructName,
  425. Description: "更新" + a.Description,
  426. ApiGroup: a.Abbreviation,
  427. Method: "PUT",
  428. },
  429. {
  430. Path: "/" + a.Abbreviation + "/" + "find" + a.StructName,
  431. Description: "根据ID获取" + a.Description,
  432. ApiGroup: a.Abbreviation,
  433. Method: "GET",
  434. },
  435. {
  436. Path: "/" + a.Abbreviation + "/" + "get" + a.StructName + "List",
  437. Description: "获取" + a.Description + "列表",
  438. ApiGroup: a.Abbreviation,
  439. Method: "GET",
  440. },
  441. }
  442. err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
  443. for _, v := range apiList {
  444. var api system.SysApi
  445. if errors.Is(tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error, gorm.ErrRecordNotFound) {
  446. if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
  447. return err
  448. } else {
  449. ids = append(ids, v.ID)
  450. }
  451. }
  452. }
  453. return nil
  454. })
  455. return ids, err
  456. }
  457. func (autoCodeService *AutoCodeService) getNeedList(autoCode *system.AutoCodeStruct) (dataList []tplData, fileList []string, needMkdir []string, err error) {
  458. // 去除所有空格
  459. utils.TrimSpace(autoCode)
  460. for _, field := range autoCode.Fields {
  461. utils.TrimSpace(field)
  462. }
  463. // 获取 basePath 文件夹下所有tpl文件
  464. tplFileList, err := autoCodeService.GetAllTplFile(autocodePath, nil)
  465. if err != nil {
  466. return nil, nil, nil, err
  467. }
  468. dataList = make([]tplData, 0, len(tplFileList))
  469. fileList = make([]string, 0, len(tplFileList))
  470. needMkdir = make([]string, 0, len(tplFileList)) // 当文件夹下存在多个tpl文件时,改为map更合理
  471. // 根据文件路径生成 tplData 结构体,待填充数据
  472. for _, value := range tplFileList {
  473. dataList = append(dataList, tplData{locationPath: value, autoPackage: autoCode.Package})
  474. }
  475. // 生成 *Template, 填充 template 字段
  476. for index, value := range dataList {
  477. dataList[index].template, err = template.ParseFiles(value.locationPath)
  478. if err != nil {
  479. return nil, nil, nil, err
  480. }
  481. }
  482. // 生成文件路径,填充 autoCodePath 字段,readme.txt.tpl不符合规则,需要特殊处理
  483. // resource/template/web/api.js.tpl -> autoCode/web/autoCode.PackageName/api/autoCode.PackageName.js
  484. // resource/template/readme.txt.tpl -> autoCode/readme.txt
  485. for index, value := range dataList {
  486. trimBase := strings.TrimPrefix(value.locationPath, autocodePath+"/")
  487. if trimBase == "readme.txt.tpl" {
  488. dataList[index].autoCodePath = autoPath + "readme.txt"
  489. continue
  490. }
  491. if lastSeparator := strings.LastIndex(trimBase, "/"); lastSeparator != -1 {
  492. origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
  493. firstDot := strings.Index(origFileName, ".")
  494. if firstDot != -1 {
  495. var fileName string
  496. if origFileName[firstDot:] != ".go" {
  497. fileName = autoCode.PackageName + origFileName[firstDot:]
  498. } else {
  499. fileName = autoCode.HumpPackageName + origFileName[firstDot:]
  500. }
  501. dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
  502. origFileName[:firstDot], fileName)
  503. }
  504. }
  505. if lastSeparator := strings.LastIndex(dataList[index].autoCodePath, string(os.PathSeparator)); lastSeparator != -1 {
  506. needMkdir = append(needMkdir, dataList[index].autoCodePath[:lastSeparator])
  507. }
  508. }
  509. for _, value := range dataList {
  510. fileList = append(fileList, value.autoCodePath)
  511. }
  512. return dataList, fileList, needMkdir, err
  513. }
  514. // injectionCode 封装代码注入
  515. func injectionCode(structName string, bf *strings.Builder) error {
  516. for _, meta := range injectionPaths {
  517. code := fmt.Sprintf(meta.structNameF, structName)
  518. if err := utils.AutoInjectionCode(meta.path, meta.funcName, code); err != nil {
  519. return err
  520. }
  521. bf.WriteString(fmt.Sprintf("%s@%s@%s;", meta.path, meta.funcName, code))
  522. }
  523. return nil
  524. }
  525. func (autoCodeService *AutoCodeService) CreateAutoCode(s *system.SysAutoCode) error {
  526. if s.PackageName == "autocode" || s.PackageName == "system" || s.PackageName == "example" || s.PackageName == "" {
  527. return errors.New("不能使用已保留的package name")
  528. }
  529. if !errors.Is(global.GVA_DB.Where("package_name = ?", s.PackageName).First(&system.SysAutoCode{}).Error, gorm.ErrRecordNotFound) {
  530. return errors.New("存在相同PackageName")
  531. }
  532. if e := autoCodeService.CreatePackageTemp(s.PackageName); e != nil {
  533. return e
  534. }
  535. return global.GVA_DB.Create(&s).Error
  536. }
  537. func (autoCodeService *AutoCodeService) GetPackage() (pkgList []system.SysAutoCode, err error) {
  538. err = global.GVA_DB.Find(&pkgList).Error
  539. return pkgList, err
  540. }
  541. func (autoCodeService *AutoCodeService) DelPackage(a system.SysAutoCode) error {
  542. return global.GVA_DB.Delete(&a).Error
  543. }
  544. func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) error {
  545. Init(packageName)
  546. pendingTemp := []autoPackage{{
  547. path: packageService,
  548. name: packageServiceName,
  549. temp: string(subcontract.Server),
  550. }, {
  551. path: packageRouter,
  552. name: packageRouterName,
  553. temp: string(subcontract.Router),
  554. }, {
  555. path: packageAPI,
  556. name: packageAPIName,
  557. temp: string(subcontract.API),
  558. }}
  559. for i, s := range pendingTemp {
  560. pendingTemp[i].path = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, filepath.Clean(fmt.Sprintf(s.path, packageName)))
  561. }
  562. // 选择模板
  563. for _, s := range pendingTemp {
  564. err := os.MkdirAll(filepath.Dir(s.path), 0755)
  565. if err != nil {
  566. return err
  567. }
  568. f, err := os.Create(s.path)
  569. if err != nil {
  570. return err
  571. }
  572. defer f.Close()
  573. temp, err := template.New("").Parse(s.temp)
  574. if err != nil {
  575. return err
  576. }
  577. err = temp.Execute(f, struct {
  578. PackageName string `json:"package_name"`
  579. }{packageName})
  580. if err != nil {
  581. return err
  582. }
  583. }
  584. // 创建完成后在对应的位置插入结构代码
  585. for _, v := range pendingTemp {
  586. meta := packageInjectionMap[v.name]
  587. if err := ImportReference(meta.path, fmt.Sprintf(meta.importCodeF, v.name, packageName), fmt.Sprintf(meta.structNameF, caser.String(packageName)), fmt.Sprintf(meta.packageNameF, packageName), meta.groupName); err != nil {
  588. return err
  589. }
  590. }
  591. return nil
  592. }
  593. type Visitor struct {
  594. ImportCode string
  595. StructName string
  596. PackageName string
  597. GroupName string
  598. }
  599. func (vi *Visitor) Visit(node ast.Node) ast.Visitor {
  600. switch n := node.(type) {
  601. case *ast.GenDecl:
  602. // 查找有没有import context包
  603. // Notice:没有考虑没有import任何包的情况
  604. if n.Tok == token.IMPORT && vi.ImportCode != "" {
  605. vi.addImport(n)
  606. // 不需要再遍历子树
  607. return nil
  608. }
  609. if n.Tok == token.TYPE && vi.StructName != "" && vi.PackageName != "" && vi.GroupName != "" {
  610. vi.addStruct(n)
  611. return nil
  612. }
  613. case *ast.FuncDecl:
  614. if n.Name.Name == "Routers" {
  615. vi.addFuncBodyVar(n)
  616. return nil
  617. }
  618. }
  619. return vi
  620. }
  621. func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor {
  622. for i := range genDecl.Specs {
  623. switch n := genDecl.Specs[i].(type) {
  624. case *ast.TypeSpec:
  625. if strings.Index(n.Name.Name, "Group") > -1 {
  626. switch t := n.Type.(type) {
  627. case *ast.StructType:
  628. f := &ast.Field{
  629. Names: []*ast.Ident{
  630. &ast.Ident{
  631. Name: vi.StructName,
  632. Obj: &ast.Object{
  633. Kind: ast.Var,
  634. Name: vi.StructName,
  635. },
  636. },
  637. },
  638. Type: &ast.SelectorExpr{
  639. X: &ast.Ident{
  640. Name: vi.PackageName,
  641. },
  642. Sel: &ast.Ident{
  643. Name: vi.GroupName,
  644. },
  645. },
  646. }
  647. t.Fields.List = append(t.Fields.List, f)
  648. }
  649. }
  650. }
  651. }
  652. return vi
  653. }
  654. func (vi *Visitor) addImport(genDecl *ast.GenDecl) ast.Visitor {
  655. // 是否已经import
  656. hasImported := false
  657. for _, v := range genDecl.Specs {
  658. importSpec := v.(*ast.ImportSpec)
  659. // 如果已经包含
  660. if importSpec.Path.Value == strconv.Quote(vi.ImportCode) {
  661. hasImported = true
  662. }
  663. }
  664. if !hasImported {
  665. genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{
  666. Path: &ast.BasicLit{
  667. Kind: token.STRING,
  668. Value: strconv.Quote(vi.ImportCode),
  669. },
  670. })
  671. }
  672. return vi
  673. }
  674. func (vi *Visitor) addFuncBodyVar(funDecl *ast.FuncDecl) ast.Visitor {
  675. hasVar := false
  676. for _, v := range funDecl.Body.List {
  677. switch varSpec := v.(type) {
  678. case *ast.AssignStmt:
  679. for i := range varSpec.Lhs {
  680. switch nn := varSpec.Lhs[i].(type) {
  681. case *ast.Ident:
  682. if nn.Name == vi.PackageName+"Router" {
  683. hasVar = true
  684. }
  685. }
  686. }
  687. }
  688. }
  689. if !hasVar {
  690. assignStmt := &ast.AssignStmt{
  691. Lhs: []ast.Expr{
  692. &ast.Ident{
  693. Name: vi.PackageName + "Router",
  694. Obj: &ast.Object{
  695. Kind: ast.Var,
  696. Name: vi.PackageName + "Router",
  697. },
  698. },
  699. },
  700. Tok: token.DEFINE,
  701. Rhs: []ast.Expr{
  702. &ast.SelectorExpr{
  703. X: &ast.SelectorExpr{
  704. X: &ast.Ident{
  705. Name: "router",
  706. },
  707. Sel: &ast.Ident{
  708. Name: "RouterGroupApp",
  709. },
  710. },
  711. Sel: &ast.Ident{
  712. Name: caser.String(vi.PackageName),
  713. },
  714. },
  715. },
  716. }
  717. funDecl.Body.List = append(funDecl.Body.List, funDecl.Body.List[1])
  718. index := 1
  719. copy(funDecl.Body.List[index+1:], funDecl.Body.List[index:])
  720. funDecl.Body.List[index] = assignStmt
  721. }
  722. return vi
  723. }
  724. func ImportReference(filepath, importCode, structName, packageName, groupName string) error {
  725. fSet := token.NewFileSet()
  726. fParser, err := parser.ParseFile(fSet, filepath, nil, parser.ParseComments)
  727. if err != nil {
  728. return err
  729. }
  730. importCode = strings.TrimSpace(importCode)
  731. v := &Visitor{
  732. ImportCode: importCode,
  733. StructName: structName,
  734. PackageName: packageName,
  735. GroupName: groupName,
  736. }
  737. if importCode == "" {
  738. ast.Print(fSet, fParser)
  739. }
  740. ast.Walk(v, fParser)
  741. var output []byte
  742. buffer := bytes.NewBuffer(output)
  743. err = format.Node(buffer, fSet, fParser)
  744. if err != nil {
  745. log.Fatal(err)
  746. }
  747. // 写回数据
  748. return ioutil.WriteFile(filepath, buffer.Bytes(), 0o600)
  749. }
  750. // CreatePlug 自动创建插件模板
  751. func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) error {
  752. // 检查列表参数是否有效
  753. plug.CheckList()
  754. tplFileList, _ := autoCodeService.GetAllTplFile(plugPath, nil)
  755. for _, tpl := range tplFileList {
  756. temp, err := template.ParseFiles(tpl)
  757. if err != nil {
  758. zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
  759. return err
  760. }
  761. pathArr := strings.SplitAfter(tpl, "/")
  762. if strings.Index(pathArr[2], "tpl") < 0 {
  763. dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[2]))
  764. os.MkdirAll(dirPath, 0755)
  765. }
  766. file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugPath):len(tpl)-4]))
  767. f, _ := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0666)
  768. err = temp.Execute(f, plug)
  769. if err != nil {
  770. zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
  771. return err
  772. }
  773. defer f.Close()
  774. }
  775. return nil
  776. }