| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- package ams_ast
- import (
- "bytes"
- "fmt"
- "github.com/pkg/errors"
- "go/ast"
- "go/format"
- "go/parser"
- "go/token"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "text/template"
- )
- // 根据模板文件 生成 service文件
- type ServiceRegister struct {
- TemplateFile string `json:"TemplateFile"` // 模块文件 resource/package/go_server/service/service.go.tpl
- TemplateEnterFile string `json:"TemplateEnterFile"` // 模块注册文件 resource/package/go_server/service/enter.go.tpl
- EnterFile string `json:"enterFile"` // 服务主入口文件 go_server/service/enter.go
- TargetEnterFile string `json:"targetEnterFile"` // 业务服务入口文件 go_server/service/alias/enter.go
- TargetFile string `json:"targetFile"` // 业务服务文件 go_server/service/alias/model.go
- ServicePkg string `json:"servicePkg"` // go_server/service/alias
- DbAlias string `json:"dbAlias"` // 数据库别名 aic_gold 模块名称
- DbName string `json:"dbName"` // 数据库名 aic_boost 数据库名
- TableName string `json:"tableName"` // 模型名称 demo_log 表名称
- ModelCap string `json:"modelCap"` // 模型名 首字母大写驼峰规则 DemoLog
- StructName string `json:"structName"` // 服务结构体名称 DemoLogService
- BaseAst
- }
- // alias 数据库别名 tableName 表名
- func BuildServiceRegister(alias, dbName, tableName string) *ServiceRegister {
- register := &ServiceRegister{
- TemplateFile: filepath.Join(template_root, server_service, "service.go.tpl"),
- TemplateEnterFile: filepath.Join(template_root, server_service, "enter.go.tpl"),
- DbAlias: alias,
- DbName: dbName,
- TableName: tableName,
- ModelCap: CapitalizeOrLower(toCamelCase(tableName)),
- StructName: fmt.Sprintf("%s%s", CapitalizeOrLower(toCamelCase(tableName)), "Service"),
- BaseAst: BaseAst{},
- }
- register.EnterFile = register.enterFile()
- register.TargetEnterFile = register.targetEnterFile(alias)
- register.TargetFile = register.targetFile(alias, tableName)
- register.ServicePkg = register.servicePkg(alias)
- return register
- }
- // 主入口服务名 BizGroup
- func (s *ServiceRegister) enterServiceGroup() string {
- return fmt.Sprintf("%s%s", CapitalizeOrLower(toCamelCase(s.DbAlias)), "ServiceGroup")
- }
- // 服务注册入口文件 go_server/service/enter.go
- func (s *ServiceRegister) enterFile() string {
- return filepath.Join(root, server_service, server_enter)
- }
- // 目标文件 go_server/service/biz/enter.go
- func (s *ServiceRegister) targetEnterFile(alias string) string {
- return filepath.Join(root, server_service, alias, server_enter)
- }
- // 目标文件 go_server/service/biz/user.go
- func (s *ServiceRegister) targetFile(alias, tableName string) string {
- return filepath.Join(root, server_service, alias, tableName+".go")
- }
- // 业务库基础服务 go_server/service/base
- func (s *ServiceRegister) baseServicePkg() string {
- return filepath.Join(server_service, server_biz_base_service)
- }
- // 服务PKG go_server/service/alias
- func (s *ServiceRegister) servicePkg(alias string) string {
- return filepath.Join(server_mod, server_service, alias)
- }
- // 初始化
- func (s *ServiceRegister) Initialize() error {
- //core.Log.Infof("服务注入开始:%s-%s", s.DbAlias, s.TableName)
- var err error
- // 创建targetService
- if err = s.createTargetService(); err != nil {
- //core.Log.Infof("服务文件创建失败:%s", err.Error())
- return err
- }
- //core.Log.Infof("1 服务文件创建完成:service/%s/%s.go", s.DbAlias, s.TableName)
- // 创建模块入口文件 注入服务
- if err = s.inspectTargetServiceToGroup(); err != nil {
- //core.Log.Infof("服务注入失败:%s", err.Error())
- return err
- }
- //core.Log.Infof("2 服务注入完成:service/%s/enter.go", s.DbAlias)
- // 注入服务到总入口文件
- if err = s.inspectRootService(); err != nil {
- //core.Log.Infof("注入服务到总入口文件失败:%s", err.Error())
- return err
- }
- //core.Log.Infof("3 注入服务到总入口文件完成:service/enter.go")
- return nil
- }
- // 创建目标服务文件
- func (s *ServiceRegister) createTargetService() error {
- var files *template.Template
- files, err := template.ParseFiles(s.TemplateFile)
- if err != nil {
- return err
- }
- err = os.MkdirAll(filepath.Dir(s.TargetFile), os.ModePerm)
- if err != nil {
- return err
- }
- if FileExists(s.TargetFile) {
- err = os.Remove(s.TargetFile)
- if err != nil {
- return err
- }
- }
- var file *os.File
- file, err = os.Create(s.TargetFile)
- if err != nil {
- return err
- }
- err = files.Execute(file, s)
- _ = file.Close()
- if err != nil {
- return err
- }
- return nil
- }
- // 注入子服务到服务组
- func (s *ServiceRegister) inspectTargetServiceToGroup() error {
- var err error
- if err = s.checkTargetEnter(); err != nil {
- return err
- }
- // 创建 token.FileSet
- fSet := token.NewFileSet()
- // 解析文件
- file, err := parser.ParseFile(fSet, s.TargetEnterFile, nil, parser.ParseComments)
- if err != nil {
- return err
- }
- // 检查是否存在 ServiceGroup 的结构体声明
- classTypeSpec, isHasServiceGroup := s.FindFileHasStruct(file, fmt.Sprintf("ServiceGroup"))
- if !isHasServiceGroup {
- return errors.New("not found ServiceGroup")
- }
- // 检查并添加属性- 不带Key
- if err = s.checkAndInsertStructFiled(classTypeSpec, s.StructName); err != nil {
- return err
- }
- // 格式化并写回文件
- var buf bytes.Buffer
- if err = format.Node(&buf, fSet, file); err != nil {
- return err
- }
- return os.WriteFile(s.TargetEnterFile, buf.Bytes(), 0666)
- }
- // 查找结构中属性 如果不存在则增加该属性 属性类型与名称一致
- func (s *ServiceRegister) checkAndInsertStructFiled(typeSpec *ast.TypeSpec, typeName string) error {
- hasField := false
- if structType, ok := typeSpec.Type.(*ast.StructType); ok {
- // 检查是否已有 AssetService 字段
- for _, field := range structType.Fields.List {
- if ident, ok := field.Type.(*ast.Ident); ok {
- if ident.Name == typeName {
- hasField = true
- break
- }
- }
- //field.Names
- }
- }
- if !hasField {
- // 添加 AssetService 字段
- structType, ok := typeSpec.Type.(*ast.StructType)
- if !ok {
- return errors.New("ServiceGroup is not a struct type")
- }
- // 创建新的字段
- newField := &ast.Field{
- Names: []*ast.Ident{
- {
- Name: "",
- },
- },
- Type: &ast.Ident{
- Name: typeName,
- },
- }
- // 添加到结构体字段列表
- structType.Fields.List = append(structType.Fields.List, newField)
- }
- return nil
- }
- // 检查与创建服务组入口文件
- func (s *ServiceRegister) checkTargetEnter() error {
- if FileExists(s.TargetEnterFile) {
- return nil
- }
- var files *template.Template
- files, err := template.ParseFiles(s.TemplateEnterFile)
- if err != nil {
- return err
- }
- err = os.MkdirAll(filepath.Dir(s.TargetEnterFile), os.ModePerm)
- if err != nil {
- return err
- }
- var file *os.File
- file, err = os.Create(s.TargetEnterFile)
- if err != nil {
- return err
- }
- err = files.Execute(file, s)
- _ = file.Close()
- if err != nil {
- return err
- }
- return nil
- }
- // 注入主服务 EnterFile
- func (s *ServiceRegister) inspectRootService() error {
- // 创建 token.FileSet
- fSet := token.NewFileSet()
- // 解析文件
- file, err := parser.ParseFile(fSet, s.EnterFile, nil, parser.ParseComments)
- if err != nil {
- log.Fatal(err)
- }
- isHasServiceGroup := s.CheckFileStructHasFiled(file, "Group", fmt.Sprintf("%sServiceGroup", s.DbAlias))
- if isHasServiceGroup {
- return nil
- }
- // 添加导入声明 -- 需要兼容windows
- normalizedPkg := filepath.ToSlash(s.ServicePkg)
- s.AddImport(file, normalizedPkg)
- // 注入服务
- // 创建新的字段
- newField := &ast.Field{
- Names: []*ast.Ident{
- {
- Name: s.enterServiceGroup(),
- },
- },
- Type: &ast.Ident{
- Name: fmt.Sprintf("%s.ServiceGroup", s.DbAlias),
- },
- }
- classTypeSpec, isHasServiceGroup := s.FindFileHasStruct(file, fmt.Sprintf("Group"))
- if !isHasServiceGroup {
- return errors.New("not found ServiceGroup")
- }
- if err = s.addFieldToStruct(classTypeSpec, s.enterServiceGroup(), newField); err != nil {
- //core.Log.Infof("服务总入口文件:%s 注入:%+v", err.Error(), newField)
- return err
- }
- // 格式化并写回文件
- var buf bytes.Buffer
- if err = format.Node(&buf, fSet, file); err != nil {
- return err
- }
- // 保持原始文件的模式
- info, err := os.Stat(s.EnterFile)
- if err != nil {
- return err
- }
- if err = ioutil.WriteFile(s.EnterFile, buf.Bytes(), info.Mode()); err != nil {
- return err
- }
- return nil
- }
- func (s *ServiceRegister) addFieldToStruct(typeSpec *ast.TypeSpec, filedName string, newField *ast.Field) error {
- hasFiled := false
- if structType, ok := typeSpec.Type.(*ast.StructType); ok {
- for _, field := range structType.Fields.List {
- for _, name := range field.Names {
- //core.Log.Infof("已注入服务:%s 待注入服务:%s", name.Name, filedName)
- if name.Name == filedName {
- hasFiled = true
- }
- }
- }
- }
- if !hasFiled {
- structType, ok := typeSpec.Type.(*ast.StructType)
- if !ok {
- return errors.New("ServiceGroup is not a struct type")
- }
- // 添加到结构体字段列表
- structType.Fields.List = append(structType.Fields.List, newField)
- }
- return nil
- }
- // 回滚
- func (s *ServiceRegister) RollBack() error {
- // delete targetService
- if FileExists(s.TargetFile) {
- err := os.Remove(s.TargetFile)
- if err != nil {
- return err
- }
- }
- if err := s.rollBackSubEnter(); err != nil {
- return err
- }
- // todo:回滚 总入口文件 -- 不需要回滚 模块一旦创建 不做回滚
- return nil
- }
- // 回滚子服务注入
- func (s *ServiceRegister) rollBackSubEnter() error {
- // 创建 token.FileSet
- fSet := token.NewFileSet()
- // 解析文件
- file, err := parser.ParseFile(fSet, s.TargetEnterFile, nil, parser.ParseComments)
- if err != nil {
- return err
- }
- // 模块入口文件 回滚注入服务
- // 检查是否存在 ServiceGroup 的结构体声明
- //core.Log.Infof("检查文件:%s中是否存在 ServiceGroup 的结构体声明", s.TargetEnterFile)
- classTypeSpec, isHasServiceGroup := s.FindFileHasStruct(file, fmt.Sprintf("ServiceGroup"))
- if !isHasServiceGroup {
- return errors.New("not found ServiceGroup")
- }
- //core.Log.Infof("检查文件:%s中 存在 ServiceGroup", s.TargetEnterFile)
- if err := s.checkAndRemoveStructFiled(classTypeSpec, s.StructName); err != nil {
- return err
- }
- // 格式化并写回文件
- var buf bytes.Buffer
- if err = format.Node(&buf, fSet, file); err != nil {
- return err
- }
- // 保持原始文件的模式
- info, err := os.Stat(s.TargetEnterFile)
- if err != nil {
- return err
- }
- if err = ioutil.WriteFile(s.TargetEnterFile, buf.Bytes(), info.Mode()); err != nil {
- return err
- }
- // 回滚 总入口文件
- return nil
- }
- // 查找结构中属性 如果存在则删除该属性
- func (s *ServiceRegister) checkAndRemoveStructFiled(typeSpec *ast.TypeSpec, typeName string) error {
- if structType, ok := typeSpec.Type.(*ast.StructType); ok {
- // 检查是否已有
- //core.Log.Infof("检查文件 ServiceGroup 中是否存在Field: %s", typeName)
- filteredFields := make([]*ast.Field, 0)
- for _, field := range structType.Fields.List {
- if ident, ok := field.Type.(*ast.Ident); ok {
- //core.Log.Infof("ident.Name:%s typeName:%s", ident.Name, typeName)
- if ident.Name == typeName {
- continue
- }
- // Skip this field (effectively removing it)
- }
- filteredFields = append(filteredFields, field)
- }
- //core.Log.Infof("len(filteredFields):%d len(structType.Fields.List):%d", len(filteredFields), len(structType.Fields.List))
- // If the length changed, we removed at least one field
- if len(filteredFields) < len(structType.Fields.List) {
- structType.Fields.List = filteredFields
- return nil
- }
- }
- return nil
- }
|