Softhub軟件下載站實戰開發(四):代碼生成器設計與實現

文章目錄

    • Softhub軟件下載站實戰開發(四):代碼生成器設計與實現
    • 1.前言 📜
    • 2.技術選型
    • 3.架構概覽 🏗?
      • 3.1 架構概覽
      • 3.2 工作流程詳解
    • 4.核心功能實現 ?
      • 4.1 配置管理系統
      • 4.2 數據庫表結構解析
      • 4.3 模板渲染引擎
      • 4.4 智能類型轉換
      • 4.5 動態文件生成
      • 4.6 智能覆蓋策略
      • 4.7 運行
    • 5.附錄 ??
      • 5.1 生成器代碼
      • 5.2 后端模板
      • 5.3 前端模板

Softhub軟件下載站實戰開發(四):代碼生成器設計與實現

1.前言 📜

在上篇文章中我們以platform模塊為例,詳細記錄了從創建model到編寫頁面的全過程。相信讀者已經對編寫流程有了一個大致的了解。
不難發現,整個項目分為多層:controller、service、dao等等,并且存在著大量模板代碼,手動一個個文件編寫費事費力,還容易出錯。GoFrame其實為我們提供了cli工具,可以方便快捷的生成例如model、service等代碼。
對整個項目而言,其實這還遠遠不夠,我們還有例如controller、service、前端等模板代碼需要編寫,這時候編寫一個簡單的代碼生成器就會方便很多。

2.技術選型

因為本項目代碼生成器不是重點,因此不考慮項目中集成,僅是作為一個工具存在。因為方便快捷作為第一考慮因素。
因此我們選用python作為主語言、結合jinja模板引擎快速生成模板代碼。

系統核心組件包括:

  1. 數據庫元數據提取 - 通過SQLAlchemy分析表結構
  2. 模板引擎 - 使用Jinja2渲染代碼模板
  3. 配置管理 - YAML配置文件支持
  4. 文件生成器 - 自動化文件創建和寫入

3.架構概覽 🏗?

3.1 架構概覽

輸出
核心處理
輸入
前端代碼生成
后端代碼生成
配置讀取模塊
數據庫連接模塊
表結構解析模塊
模板渲染引擎
配置文件 application.yml
前端項目
后端項目

3.2 工作流程詳解

Main ConfigUtil DB Inspector Template Engine File Writer 讀取application.yml 返回配置數據 連接數據庫 獲取表結構信息 返回表元數據 加載前端模板 渲染模板 返回渲染結果 寫入前端文件 alt [生成前端代碼] 加載后端模板 渲染模板 返回渲染結果 寫入后端文件 alt [生成后端代碼] loop [遍歷每個表配置] Main ConfigUtil DB Inspector Template Engine File Writer

4.核心功能實現 ?

4.1 配置管理系統

database:host: 127.0.0.1port: 3306user: rootpassword: 123456dbname: softhubcode_generator:# 生成類型:frontend-前端, backend-后端, all-全部generate_type: all# 表配置tables:- name: ds_platform  # 表名comment: "平臺管理"  # 表注釋generate_type: all  # 可選:frontend, backend, allfrontend:template_name: "gfast前端模板2"output_path: "D:/output/frontend"primary_name: "name"  # 主鍵名稱backend:template_name: "gfast模板"output_path: "D:/output/backend"package: "platform"  # 包名package_path: "github.com/tiger1103/gfast/v3"  # 包路徑前綴

配置模塊管理

class ConfigUtil:def __init__(self, config_path: Path):self.config_path = config_pathdef read_config(self) -> Dict[str, Any]:with open(self.config_path, 'r', encoding='utf-8') as f:return yaml.safe_load(f)

使用YAML配置文件管理:

  1. 數據庫連接信息
  2. 模板路徑配置
  3. 包名和導入路徑
  4. 代碼生成選項

4.2 數據庫表結構解析

class InspectUtils:def __init__(self, engine):self.engine = engineself.inspector = inspect(engine)def get_infos(self, table_name: str) -> Dict[str, Any]:columns = self.inspector.get_columns(table_name)# 處理字段信息...return {'table_name': table_name,'columns': columns,# 其他元數據...}
  • 使用SQLAlchemy的Inspector獲取表元數據
  • 自動將SQL類型轉換為Go類型
  • 生成駝峰命名等符合編程規范的字段名

4.3 模板渲染引擎

# 設置模板路徑
template_base_path = Path('template')
template_path = template_base_path.joinpath(template_name)# 渲染模板
templates = Environment(loader=FileSystemLoader(template_path), lstrip_blocks=True, trim_blocks=True)
tl = templates.get_template(template)
render_str = tl.render(infos)
  • 支持模板繼承和宏定義
  • 自動處理縮進和空白字符
  • 動態生成文件路徑和文件名

4.4 智能類型轉換

def _get_go_type(self, sql_type: str) -> str:"""將SQL類型轉換為Go類型"""sql_type = sql_type.lower()if 'int' in sql_type:return 'int'elif 'varchar' in sql_type or 'char' in sql_type or 'text' in sql_type:return 'string'elif 'time' in sql_type or 'date' in sql_type:return '*gtime.Time'# 其他類型處理...

4.5 動態文件生成

# 渲染相對路徑
relative_path = Template(str(item_template_path.parent.relative_to(template_path)).render(file_info)
# 渲染文件名
template_stem = item_template_path.stem
if '.' in template_stem:name_part, ext = template_stem.rsplit('.', 1)file_name = Template(name_part).render(file_info) + '.' + ext

4.6 智能覆蓋策略

# 跳過已存在的router.go和logic.go文件
if Path(file_path).exists() and (str(item_template_path.stem) in ["router.go", "logic.go"]):continue

4.7 運行

修改config\application.yml配置

python code_generator.py

輸出類似如下
image.png

5.附錄 ??

5.1 生成器代碼

import logging as log
import os
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, Listfrom jinja2 import Environment, FileSystemLoader, Template
import sqlalchemy as sa
from sqlalchemy import inspect
import yaml# 配置日志
log.basicConfig(level=log.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')class ConfigUtil:def __init__(self, config_path: Path):self.config_path = config_pathdef read_config(self) -> Dict[str, Any]:with open(self.config_path, 'r', encoding='utf-8') as f:return yaml.safe_load(f)class InspectUtils:def __init__(self, engine):self.engine = engineself.inspector = inspect(engine)self.tables = self.inspector.get_table_names()def get_infos(self, table_name: str) -> Dict[str, Any]:columns = self.inspector.get_columns(table_name)primary_keys = self.inspector.get_pk_constraint(table_name)foreign_keys = self.inspector.get_foreign_keys(table_name)# 處理字段信息field_info = []for column in columns:# 獲取字段類型type_name = str(column['type'])go_type = self._get_go_type(type_name)# 處理字段名name = column['name']camel_name = ''.join(word.capitalize() for word in name.split('_'))if camel_name and camel_name[0].isupper():camel_name = camel_name[0].lower() + camel_name[1:]field_info.append({'name': name,'camelName': camel_name,'firstUpperName': ''.join(word.capitalize() for word in name.split('_')),'type': go_type,'comment': column.get('comment', ''),'nullable': column.get('nullable', False)})return {'table_name': table_name,'columns': columns,'primary_keys': primary_keys,'foreign_keys': foreign_keys,'field_info': field_info}def _get_go_type(self, sql_type: str) -> str:"""將SQL類型轉換為Go類型"""sql_type = sql_type.lower()if 'int' in sql_type:return 'int'elif 'varchar' in sql_type or 'char' in sql_type or 'text' in sql_type:return 'string'elif 'float' in sql_type or 'double' in sql_type or 'decimal' in sql_type:return 'float64'elif 'bool' in sql_type:return 'bool'elif 'time' in sql_type or 'date' in sql_type:return '*gtime.Time'else:return 'string'def get_file_info(self, table_name: str) -> Dict[str, str]:first_upper_name = ''.join(word.capitalize() for word in table_name.split('_'))return {'table_name': table_name,'table_name_camel': ''.join(word.capitalize() for word in table_name.split('_')),'table_name_lower': table_name.lower(),'name': table_name,'first_upper_name': first_upper_name}def get_connection(config: Dict[str, Any]):return sa.create_engine(f"mysql+pymysql://{config['user']}:{config['password']}@{config['host']}:{config['port']}/{config['dbname']}")def generate_code(config_map: Dict[str, Any], table_config: Dict[str, Any]):template_name = config_map['template_name']table_name = config_map['table_name']output_path = config_map['output_path']package = config_map.get('package', 'template')package_path = config_map.get('package_path', 'github.com/tiger1103/gfast/v3')# 讀取配置config_path = Path('config/application.yml').resolve()config_util = ConfigUtil(config_path)config = config_util.read_config()# 連接數據庫engine = get_connection(config['database'])iu = InspectUtils(engine)# 獲取表信息infos = iu.get_infos(table_name)# 添加前端信息front_info = {'search_columns': [{"camelName": "name", "comment": "變量名稱", "type": "string"}]}if "primary_name" in config_map:front_info['primaryName'] = config_map['primary_name']infos['front_info'] = front_info# 添加包信息package_info = {'api': f"{package_path}/api/v1/{package}",'service': f"{package_path}/internal/app/{package}/service",'dao': f"{package_path}/internal/app/{package}/dao",'liberr': f"{package_path}/library/liberr",'libUtils': f"{package_path}/library/libUtils",'consts': f"{package_path}/internal/app/system/consts",'model': f"{package_path}/internal/app/{package}/model",'entity': f"{package_path}/internal/app/{package}/model/entity",'utils': f"{package_path}/internal/app/{package}/utils",'common': f"{package_path}/internal/app/{package}/common",'commonDao': f"{package_path}/internal/app/common/dao",'commonEntity': f"{package_path}/internal/app/common/model/entity",'commonApi': f"{package_path}/api/v1/common",'do': f"{package_path}/internal/app/{package}/model/do",'SystemS': f"{package_path}/internal/app/system/service",'dao_internal': f"{package_path}/internal/app/{package}/dao/internal",'package': package,'packageFirstUpper': package.capitalize()}infos['package_info'] = package_info# 獲取文件信息file_info = iu.get_file_info(table_name)file_info['package'] = packagefile_info['packageFirstUpper'] = package.capitalize()# 添加表信息table_info = {'name': table_name,'lowerName': table_name.lower(),'upperName': table_name.upper(),'firstUpperName': ''.join(word.capitalize() for word in table_name.split('_')),'camelName': ''.join(word.capitalize() for word in table_name.split('_'))[0].lower() + ''.join(word.capitalize() for word in table_name.split('_'))[1:],'comment': table_config.get('comment', ''),'columns': infos['columns'],'primaryKeys': infos['primary_keys'],'foreignKeys': infos['foreign_keys']}infos['table_info'] = table_info# 設置模板路徑template_base_path = Path('template')template_path = template_base_path.joinpath(template_name)# 設置輸出路徑output_path = Path(output_path).resolve()if not output_path.exists():output_path.mkdir(parents=True)# 渲染模板templates = Environment(loader=FileSystemLoader(template_path), lstrip_blocks=True, trim_blocks=True)for template in templates.list_templates():tl = templates.get_template(template)item_template_path = Path(tl.filename)# 渲染相對路徑relative_path = Template(str(item_template_path.parent.relative_to(template_path))).render(file_info)log.info(f"Processing template: {relative_path}")# 渲染文件名(保持原始擴展名)template_stem = item_template_path.stem  # 獲取不帶.j2的文件名if '.' in template_stem:  # 如果文件名中包含擴展名name_part, ext = template_stem.rsplit('.', 1)  # 分離名稱和擴展名file_name = Template(name_part).render(file_info) + '.' + extelse:file_name = Template(template_stem).render(file_info)render_str = tl.render(infos)parent_output = output_path.joinpath(relative_path)if not parent_output.exists():parent_output.mkdir(parents=True)file_path = parent_output.joinpath(file_name)log.info(f"Generating file: {file_path}")# 跳過已存在的router.go和logic.go文件if Path(file_path).exists() and (str(item_template_path.stem) in ["router.go", "logic.go"]):continuewith open(file_path, 'w', encoding='utf-8') as f:f.write(render_str)def main():# 讀取配置config_path = Path('config/application.yml').resolve()config_util = ConfigUtil(config_path)config = config_util.read_config()# 獲取生成器配置generator_config = config['code_generator']generate_type = generator_config['generate_type']# 處理每個表的配置for table_config in generator_config['tables']:table_name = table_config['name']table_generate_type = table_config.get('generate_type', generate_type)# 生成前端代碼if table_generate_type in ['frontend', 'all']:frontend_config = table_config['frontend']config_map = {'template_name': frontend_config['template_name'],'table_name': table_name,'output_path': frontend_config['output_path'],'primary_name': frontend_config['primary_name']}generate_code(config_map, table_config)# 生成后端代碼if table_generate_type in ['backend', 'all']:backend_config = table_config['backend']config_map = {'template_name': backend_config['template_name'],'table_name': table_name,'output_path': backend_config['output_path'],'package': backend_config['package'],'package_path': backend_config['package_path']}generate_code(config_map, table_config)if __name__ == "__main__":main() 

5.2 后端模板

項目結構

gfast模板:
- api
- api\v1
- api\v1\{{package}}
- api\v1\{{package}}\{{name}}.go.j2
- internal
- internal\app
- internal\app\{{package}}
- internal\app\{{package}}\controller
- internal\app\{{package}}\controller\{{name}}.go.j2
- internal\app\{{package}}\dao
- internal\app\{{package}}\dao\internal
- internal\app\{{package}}\dao\internal\{{name}}.go.j2
- internal\app\{{package}}\dao\{{name}}.go.j2
- internal\app\{{package}}\logic
- internal\app\{{package}}\logic\logic.go.j2
- internal\app\{{package}}\logic\{{name}}
- internal\app\{{package}}\logic\{{name}}\{{name}}.go.j2
- internal\app\{{package}}\model
- internal\app\{{package}}\model\do
- internal\app\{{package}}\model\do\{{name}}.go.j2
- internal\app\{{package}}\model\entity
- internal\app\{{package}}\model\entity\{{name}}.go.j2
- internal\app\{{package}}\model\{{name}}.go.j2
- internal\app\{{package}}\router
- internal\app\{{package}}\router\router.go.j2
- internal\app\{{package}}\service
- internal\app\{{package}}\service\{{name}}.go.j2

代碼內容

api\v1{{package}}{{name}}.go.j2

package templateimport ("github.com/gogf/gf/v2/frame/g"commonApi "{{package_info.commonApi}}"model "{{package_info.model}}")type {{table_info.firstUpperName}}AddReq struct {g.Meta `path:"/{{table_info.camelName}}/add" method:"post" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-新增"`{% for item in field_info %}{% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}" v:"required#{{ item.comment }}不能為空"`{% endif %}{% endfor %}
}type {{table_info.firstUpperName}}AddRes struct {g.Meta `mime:"application/json" example:"string"`
}type {{table_info.firstUpperName}}DelReq struct {g.Meta `path:"/{{table_info.camelName}}/del" method:"delete" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-刪除"`Id uint `json:"id" v:"required#id不能為空"`
}type {{table_info.firstUpperName}}DelRes struct {g.Meta `mime:"application/json" example:"string"`
}type {{table_info.firstUpperName}}BatchDelReq struct {g.Meta `path:"/{{table_info.camelName}}/batchdel" method:"delete" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-批量刪除"`Ids []uint `json:"id" v:"required#id不能為空"`
}type {{table_info.firstUpperName}}BatchDelRes struct {g.Meta `mime:"application/json" example:"string"`
}type {{table_info.firstUpperName}}EditReq struct {g.Meta `path:"/{{table_info.camelName}}/edit" method:"put" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-修改"`{% for item in field_info %}{% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}" v:"required#{{ item.comment }}不能為空"`{% endif %}{% endfor %}
}type {{table_info.firstUpperName}}EditRes struct {g.Meta `mime:"application/json" example:"string"`
}type {{table_info.firstUpperName}}ListReq struct {g.Meta `path:"/{{table_info.camelName}}/list" method:"get" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-列表"`commonApi.PageReq{% for item in field_info %}{% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}" v:"required#{{ item.comment }}不能為空"`{% endif %}{% endfor %}
}type {{table_info.firstUpperName}}ListRes struct {g.Meta `mime:"application/json" example:"string"`commonApi.ListRes{{table_info.firstUpperName}}List []*model.{{table_info.firstUpperName}}Info `json:"{{table_info.camelName}}List"`
}type {{table_info.firstUpperName}}DetailReq struct {g.Meta `path:"/{{table_info.camelName}}/detail" method:"get" tags:"{{table_info.comment}}" summary:"{{table_info.comment}}-詳情"`Id uint `json:"id" v:"required#id不能為空"`
}type {{table_info.firstUpperName}}DetailRes struct {g.Meta `mime:"application/json" example:"string"`*model.{{table_info.firstUpperName}}Info
}

internal\app{{package}}\controller{{name}}.go.j2

package controllerimport ("context"api "{{package_info.api}}"service "{{package_info.service}}"consts "{{package_info.consts}}"
)var {{table_info.firstUpperName}} = {{table_info.camelName}}Controller{}type {{table_info.camelName}}Controller struct {BaseController
}func (c *{{table_info.camelName}}Controller) Add(ctx context.Context, req *api.{{table_info.firstUpperName}}AddReq) (res *api.{{table_info.firstUpperName}}AddRes, err error) {res = new(api.{{table_info.firstUpperName}}AddRes)err = service.{{table_info.firstUpperName}}().Add(ctx, req)return
}func (c *{{table_info.camelName}}Controller) List(ctx context.Context, req *api.{{table_info.firstUpperName}}ListReq) (res *api.{{table_info.firstUpperName}}ListRes, err error) {res = new(api.{{table_info.firstUpperName}}ListRes)if req.PageSize == 0 {req.PageSize = consts.PageSize}if req.PageNum == 0 {req.PageNum = 1}total, {{table_info.camelName}}s, err := service.{{table_info.firstUpperName}}().List(ctx, req)res.Total = totalres.CurrentPage = req.PageNumres.{{table_info.firstUpperName}}List = {{table_info.camelName}}sreturn
}func (c *{{table_info.camelName}}Controller) Get(ctx context.Context, req *api.{{table_info.firstUpperName}}DetailReq) (res *api.{{table_info.firstUpperName}}DetailRes, err error) {res = new(api.{{table_info.firstUpperName}}DetailRes)service.{{table_info.firstUpperName}}().GetById(ctx, req.Id)return
}func (c *{{table_info.camelName}}Controller) Edit(ctx context.Context, req *api.{{table_info.firstUpperName}}EditReq) (res *api.{{table_info.firstUpperName}}EditRes, err error) {err = service.{{table_info.firstUpperName}}().Edit(ctx, req)return
}func (c *{{table_info.camelName}}Controller) Delete(ctx context.Context, req *api.{{table_info.firstUpperName}}DelReq) (res *api.{{table_info.firstUpperName}}DelRes, err error) {err = service.{{table_info.firstUpperName}}().Delete(ctx, req.Id)return
}func (c *{{table_info.camelName}}Controller) BatchDelete(ctx context.Context, req *api.{{table_info.firstUpperName}}BatchDelReq) (res *api.{{table_info.firstUpperName}}BatchDelRes, err error) {err = service.{{table_info.firstUpperName}}().BatchDelete(ctx, req.Ids)return
}

internal\app{{package}}\dao\internal{{name}}.go.j2

// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================package internalimport ("context""github.com/gogf/gf/v2/database/gdb""github.com/gogf/gf/v2/frame/g"
)// {{table_info.firstUpperName}}Dao is the data access object for table cts_voice.
type {{table_info.firstUpperName}}Dao struct {table   string          // table is the underlying table name of the DAO.group   string          // group is the database configuration group name of current DAO.columns {{table_info.firstUpperName}}Columns // columns contains all the column names of Table for convenient usage.
}// {{table_info.firstUpperName}}Columns defines and stores column names for table {{table_info.Name}}.
type {{table_info.firstUpperName}}Columns struct {{% for item in field_info %}{{ item.firstUpperName }} string //{{ item.comment }}{% endfor %}
}// {{table_info.camelName}}Columns holds the columns for table {{table_info.Name}}.
var {{table_info.camelName}}Columns = {{table_info.firstUpperName}}Columns{{% for item in field_info %}{{ item.firstUpperName }}: "{{ item.name }}",{% endfor %}
}// New{{table_info.firstUpperName}}Dao creates and returns a new DAO object for table data access.
func New{{table_info.firstUpperName}}Dao() *{{table_info.firstUpperName}}Dao {return &{{table_info.firstUpperName}}Dao{group:   "default",table:   "{{table_info.name}}",columns: {{table_info.camelName}}Columns,}
}// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *{{table_info.firstUpperName}}Dao) DB() gdb.DB {return g.DB(dao.group)
}// Table returns the table name of current dao.
func (dao *{{table_info.firstUpperName}}Dao) Table() string {return dao.table
}// Columns returns all column names of current dao.
func (dao *{{table_info.firstUpperName}}Dao) Columns() {{table_info.firstUpperName}}Columns {return dao.columns
}// Group returns the configuration group name of database of current dao.
func (dao *{{table_info.firstUpperName}}Dao) Group() string {return dao.group
}// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *{{table_info.firstUpperName}}Dao) Ctx(ctx context.Context) *gdb.Model {return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *{{table_info.firstUpperName}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {return dao.Ctx(ctx).Transaction(ctx, f)
}

internal\app{{package}}\dao{{name}}.go.j2

// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================package daoimport (internal "{{package_info.dao_internal}}"
)// internal{{table_info.firstUpperName}}Dao is internal type for wrapping internal DAO implements.
type internal{{table_info.firstUpperName}}Dao = *internal.{{table_info.firstUpperName}}Dao// {{table_info.camelName}}Dao is the data access object for table {{table_info.Name}}.
// You can define custom methods on it to extend its functionality as you wish.
type {{table_info.camelName}}Dao struct {internal{{table_info.firstUpperName}}Dao
}var (// {{table_info.firstUpperName}}Dao is globally public accessible object for table cts_voice operations.{{table_info.firstUpperName}} = {{table_info.camelName}}Dao{internal.New{{table_info.firstUpperName}}Dao(),}
)// Fill with you ideas below.

internal\app{{package}}\logic\logic.go.j2

package logicimport _ "github.com/tiger1103/gfast/v3/internal/app/{{package_info.package}}/logic/{{table_info.name}}"

internal\app{{package}}\logic{{name}}{{name}}.go.j2

package {{table_info.name}}import ("context""fmt""github.com/gogf/gf/v2/frame/g"api "{{package_info.api}}"dao "{{package_info.dao}}"model "{{package_info.model}}"do "{{package_info.do}}"service "{{package_info.service}}"SystemS "{{package_info.SystemS}}"liberr "{{package_info.liberr}}"
)func init() {service.Register{{table_info.firstUpperName}}(New())
}func New() *s{{table_info.firstUpperName}} {return &s{{table_info.firstUpperName}}{}
}type s{{table_info.firstUpperName}} struct {
}func (s s{{table_info.firstUpperName}}) List(ctx context.Context, req *api.{{table_info.firstUpperName}}ListReq) (total interface{}, {{table_info.camelName}}List []*model.{{table_info.firstUpperName}}Info, err error) {err = g.Try(ctx, func(ctx context.Context) {m := dao.{{table_info.firstUpperName}}.Ctx(ctx)columns := dao.{{table_info.firstUpperName}}.Columns()//TODO 根據實際情況修改{% for item in field_info %}{% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}if req.{{ item.firstUpperName }} != "" {m = m.Where(columns.{{ item.firstUpperName }}+" = ?", req.{{ item.firstUpperName }})// like//m = m.Where(fmt.Sprintf("%s like ?", columns.{{ item.firstUpperName }}), "%"+req.{{ item.firstUpperName }}+"%")}{% endif %}{% endfor %}total, err = m.Count()liberr.ErrIsNil(ctx, err, "獲取{{table_info.comment}}列表失敗")orderBy := req.OrderByif orderBy == "" {orderBy = "created_at desc"}err = m.Page(req.PageNum, req.PageSize).Order(orderBy).Scan(&{{table_info.camelName}}List)liberr.ErrIsNil(ctx, err, "獲取{{table_info.comment}}列表失敗")})return
}func (s s{{table_info.firstUpperName}}) Add(ctx context.Context, req *api.{{table_info.firstUpperName}}AddReq) (err error) {err = g.Try(ctx, func(ctx context.Context) {// TODO 查詢是否已經存在// add_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).Insert(do.{{table_info.firstUpperName}}{{% for item in field_info %}{% if item.camelName not in ['id','createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.firstUpperName }}:  req.{{ item.firstUpperName }},    // {{ item.comment }}{% endif %}{% endfor %}CreatedBy: SystemS.Context().GetUserId(ctx),UpdatedBy: SystemS.Context().GetUserId(ctx),})liberr.ErrIsNil(ctx, err, "新增{{table_info.comment}}失敗")})return
}func (s s{{table_info.firstUpperName}}) Edit(ctx context.Context, req *api.{{table_info.firstUpperName}}EditReq) (err error) {err = g.Try(ctx, func(ctx context.Context) {_, err = s.GetById(ctx, req.Id)liberr.ErrIsNil(ctx, err, "獲取{{table_info.comment}}失敗")//TODO 根據名稱等查詢是否存在//編輯_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).WherePri(req.Id).Update(do.{{table_info.firstUpperName}}{{% for item in field_info %}{% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.firstUpperName }}:  req.{{ item.firstUpperName }},    // {{ item.comment }}{% endif %}{% endfor %}})liberr.ErrIsNil(ctx, err, "修改{{table_info.comment}}失敗")})return
}func (s s{{table_info.firstUpperName}}) Delete(ctx context.Context, id uint) (err error) {err = g.Try(ctx, func(ctx context.Context) {_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).WherePri(id).Delete()liberr.ErrIsNil(ctx, err, "刪除{{table_info.comment}}失敗")})return
}func (s s{{table_info.firstUpperName}}) BatchDelete(ctx context.Context, ids []uint) (err error) {err = g.Try(ctx, func(ctx context.Context) {_, err = dao.{{table_info.firstUpperName}}.Ctx(ctx).Where(dao.{{table_info.firstUpperName}}.Columns().Id+" in(?)", ids).Delete()liberr.ErrIsNil(ctx, err, "批量刪除{{table_info.comment}}失敗")})return
}func (s s{{table_info.firstUpperName}}) GetById(ctx context.Context, id uint) (res *model.{{table_info.firstUpperName}}Info, err error) {err = g.Try(ctx, func(ctx context.Context) {err = dao.{{table_info.firstUpperName}}.Ctx(ctx).Where(fmt.Sprintf("%s=?", dao.{{table_info.firstUpperName}}.Columns().Id), id).Scan(&res)liberr.ErrIsNil(ctx, err, "獲取{{table_info.comment}}失敗")})return
}

internal\app{{package}}\model\do{{name}}.go.j2

// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================package doimport ("github.com/gogf/gf/v2/frame/g"
)// {{table_info.firstUpperName}} is the golang structure of table {{table_info.name}} for DAO operations like Where/Data.
type {{table_info.firstUpperName}} struct {g.Meta    `orm:"table:{{table_info.name}}, do:true"`{% for item in field_info %}{{ item.firstUpperName }} interface{} //{{ item.comment }}{% endfor %}
}

internal\app{{package}}\model\entity{{name}}.go.j2

// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================package entityimport "github.com/gogf/gf/v2/os/gtime"// {{table_info.firstUpperName}} is the golang structure for table {{table_info.name}}.
type {{table_info.firstUpperName}} struct {{% for item in field_info %}{{ item.firstUpperName }} {{ item.type }} `json:"{{ item.camelName }}"   description:"{{ item.comment }}"`{% endfor %}
}

internal\app{{package}}\model{{name}}.go.j2

package modelimport "github.com/gogf/gf/v2/os/gtime"type {{table_info.firstUpperName}}Info struct {{% for item in field_info %}{{ item.firstUpperName }} {{ item.type }} `orm:"{{item.name}}"  json:"{{item.camelName}}"` // {{ item.comment }}{% endfor %}
}

internal\app{{package}}\router\router.go.j2

/*
* @desc:后臺路由
* @company:云南奇訊科技有限公司
* @Author: yixiaohu
* @Date:   2022/2/18 17:34*/package routerimport ("context""github.com/gogf/gf/v2/net/ghttp"_ "github.com/tiger1103/gfast/v3/internal/app/{{package_info.package}}/logic""github.com/tiger1103/gfast/v3/internal/app/{{package_info.package}}/controller""github.com/tiger1103/gfast/v3/internal/app/system/service""github.com/tiger1103/gfast/v3/library/libRouter"
)var R = new(Router)type Router struct{}func (router *Router) BindController(ctx context.Context, group *ghttp.RouterGroup) {group.Group("/{{package_info.package}}", func(group *ghttp.RouterGroup) {//登錄驗證攔截service.GfToken().Middleware(group)//context攔截器group.Middleware(service.Middleware().Ctx, service.Middleware().Auth)//后臺操作日志記錄group.Hook("/*", ghttp.HookAfterOutput, service.OperateLog().OperationLog)group.Bind(controller.{{table_info.firstUpperName}},)//自動綁定定義的控制器if err := libRouter.RouterAutoBind(ctx, router, group); err != nil {panic(err)}})
}

internal\app{{package}}\service{{name}}.go.j2

package serviceimport ("context"api "{{package_info.api}}"model "{{package_info.model}}"
)type I{{table_info.firstUpperName}} interface {List(ctx context.Context, req *api.{{table_info.firstUpperName}}ListReq) (total interface{}, res []*model.{{table_info.firstUpperName}}Info, err error)Add(ctx context.Context, req *api.{{table_info.firstUpperName}}AddReq) (err error)Edit(ctx context.Context, req *api.{{table_info.firstUpperName}}EditReq) (err error)Delete(ctx context.Context, id uint) (err error)BatchDelete(ctx context.Context, ids []uint) (err error)GetById(ctx context.Context, id uint) (res *model.{{table_info.firstUpperName}}Info, err error)
}var local{{table_info.firstUpperName}} I{{table_info.firstUpperName}}func {{table_info.firstUpperName}}() I{{table_info.firstUpperName}} {if local{{table_info.firstUpperName}} == nil {panic("implement not found for interface I{{table_info.firstUpperName}}, forgot register?")}return local{{table_info.firstUpperName}}
}func Register{{table_info.firstUpperName}}(i I{{table_info.firstUpperName}}) {local{{table_info.firstUpperName}} = i
}

5.3 前端模板

項目結構

gfast前端模板2:
- src
- src\api
- src\api\{{package}}
- src\api\{{package}}\{{camel_name}}
- src\api\{{package}}\{{camel_name}}\index.ts.j2
- src\views
- src\views\{{package}}
- src\views\{{package}}\{{camel_name}}
- src\views\{{package}}\{{camel_name}}\component
- src\views\{{package}}\{{camel_name}}\component\edit{{first_upper_name}}.vue.j2
- src\views\{{package}}\{{camel_name}}\index.vue.j2

代碼內容

src\api{{package}}{{camel_name}}\index.ts.j2

import request from '/@/utils/request';export function get{{table_info.firstUpperName}}List(query?:Object) {return request({url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/list',method: 'get',params:query})
}export function add{{table_info.firstUpperName}}(data:object) {return request({url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/add',method: 'post',data:data})
}export function edit{{table_info.firstUpperName}}(data:object) {return request({url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/edit',method: 'put',data:data})
}export function delete{{table_info.firstUpperName}}(id:number) {return request({url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/del',method: 'delete',data:{id}})
}export function batchDelete{{table_info.firstUpperName}}(ids:number[]) {return request({url: '/api/v1/{{package_info.package}}/{{table_info.camelName}}/batchdel',method: 'delete',data:{ids}})
}

src\views{{package}}{{camel_name}}\component\edit{{first_upper_name}}.vue.j2

<template><div class="system-edit-post-container"><el-dialog v-model="state.isShowDialog" width="769px"><template #header><div>{{'{{'}}(state.formData.id===0?'添加':'修改')+'{{table_info.comment}}'{{'}}'}}</div></template><el-form ref="formRef" :model="state.formData" :rules="state.rules" size="default" label-width="90px">{% for item in field_info %}{% if item.camelName not in ['id', 'createdBy','updatedBy','createdAt','updatedAt'] %}<el-form-item label="{{ item.comment }}" prop="{{ item.camelName }}"><el-input v-model="state.formData.{{ item.camelName }}" placeholder="請輸入{{ item.comment }}名稱" clearable /></el-form-item>{% endif %}{% endfor %}</el-form><template #footer><span class="dialog-footer"><el-button @click="onCancel" size="default">取 消</el-button><el-button type="primary" @click="onSubmit" size="default" :loading="state.loading">{{'{{'}}state.formData.id===0?'新 增':'修 改'{{'}}'}}</el-button></span></template></el-dialog></div>
</template><script setup lang="ts">
import { ref, reactive } from 'vue';
import { add{{ table_info.firstUpperName }}, edit{{ table_info.firstUpperName }} } from "/@/api/{{ package_info.package }}/{{ table_info.camelName }}/index.ts";
import { ElMessage, ElForm } from 'element-plus';interface DialogRow {{% for item in field_info %}{% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.camelName }}: {{ item.tsType }}; // {{ item.comment }}{% endif %}{% endfor %}
}const formRef = ref<InstanceType<typeof ElForm> | null>(null);
const state = reactive({loading: false,isShowDialog: false,formData: {{% for item in field_info %}{% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}{% if item.tsType == 'string' %}{{ item.camelName }}: '', // {{ item.comment }}{% else %}{{ item.camelName }}: 0, // {{ item.comment }}{% endif %}{% endif %}{% endfor %}} as DialogRow,rules: {{% for item in field_info %}{% if item.camelName not in ['id', 'createdBy','updatedBy','createdAt','updatedAt'] %}{{ item.camelName }}: [{ required: true, message: "{{ item.comment }}不能為空", trigger: "blur" }],{% endif %}{% endfor %}},
});const openDialog = (row?: DialogRow) => {resetForm();if (row) {state.formData = row;}state.isShowDialog = true;
};const closeDialog = () => {state.isShowDialog = false;
};const onCancel = () => {closeDialog();
};const onSubmit = () => {const formWrap = formRef.value;if (!formWrap) {return;}formWrap.validate((valid: boolean) => {if (!valid) {return;}state.loading = true;if (state.formData.id === 0) {add{{ table_info.firstUpperName }}(state.formData).then(() => {ElMessage.success('{{ table_info.comment }}添加成功');closeDialog();emit('get{{ table_info.firstUpperName }}List');}).finally(() => {state.loading = false;});} else {edit{{ table_info.firstUpperName }}(state.formData).then(() => {ElMessage.success('{{ table_info.comment }}編輯成功');closeDialog();emit('get{{ table_info.firstUpperName }}List');}).finally(() => {state.loading = false;});}});
};const resetForm = () => {state.formData = {{% for item in field_info %}{% if item.camelName not in ['createdBy','updatedBy','createdAt','updatedAt'] %}{% if item.tsType == 'string' %}{{ item.camelName }}: '', // {{ item.comment }}{% else %}{{ item.camelName }}: 0, // {{ item.comment }}{% endif %}{% endif %}{% endfor %}} as DialogRow;if (formRef.value) {formRef.value.resetFields();}
};defineExpose({openDialog,closeDialog,
});const emit = defineEmits(['get{{ table_info.firstUpperName }}List']);
</script><style scoped lang="scss">
</style>

src\views{{package}}{{camel_name}}\index.vue.j2

<template><div class="system-{{table_info.camelName}}-container"><el-card shadow="hover"><div class="system-{{table_info.camelName}}-search mb15"><el-form :inline="true">{% for item in front_info.serach_columns %}<el-form-item label="{{item.comment}}"><el-input size="default" v-model="state.tableData.param.{{item.camelName}}" style="width: 240px" placeholder="請輸入{{item.comment}}" class="w-50 m-2" clearable/></el-form-item>{% endfor %}<el-form-item><el-button size="default" type="primary" class="ml10" @click="{{table_info.camelName}}List"><el-icon><ele-Search /></el-icon>查詢</el-button><el-button size="default" type="success" class="ml10" @click="onOpenAdd{{table_info.firstUpperName}}"><el-icon><ele-FolderAdd /></el-icon>新增{{table_info.comment}}</el-button><el-button size="default" type="danger" class="ml10" @click="onRowDel(null)"><el-icon><ele-Delete /></el-icon>刪除{{table_info.comment}}</el-button></el-form-item></el-form></div><el-table :data="state.tableData.data" style="width: 100%" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column type="index" label="序號" width="60" />{% for item in field_info %}{% if item.camelName not in ['id', 'createdBy','updatedBy','createdAt','updatedAt'] %}<el-table-column prop="{{item.camelName}}" label="{{item.comment}}" show-overflow-tooltip></el-table-column>{% endif %}{% endfor %}<el-table-column label="操作" width="200"><template #default="scope"><el-button size="small" text type="primary" @click="onOpenEdit{{table_info.firstUpperName}}(scope.row)">修改</el-button><el-button size="small" text type="primary" @click="onRowDel(scope.row)">刪除</el-button></template></el-table-column></el-table><paginationv-show="state.tableData.total>0":total="state.tableData.total"v-model:page="state.tableData.param.pageNum"v-model:limit="state.tableData.param.pageSize"@pagination="{{table_info.camelName}}List"/></el-card><Edit{{table_info.firstUpperName}} ref="edit{{table_info.firstUpperName}}Ref" @get{{table_info.firstUpperName}}List="{{table_info.camelName}}List"/></div>
</template><script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import Edit{{table_info.firstUpperName}} from '/@/views/{{package_info.package}}/{{table_info.camelName}}/component/edit{{table_info.firstUpperName}}.vue';
import { batchDelete{{table_info.firstUpperName}}, get{{table_info.firstUpperName}}List } from "/@/api/{{package_info.package}}/{{table_info.camelName}}";// 定義接口來定義對象的類型
interface TableData {{% for item in field_info %}{{ item.camelName }}: {{ item.tsType }}; // {{ item.comment }}{% endfor %}
}interface TableDataState {ids: number[];tableData: {data: Array<TableData>;total: number;loading: boolean;param: {{% for item in front_info.serach_columns %}{{ item.camelName }}: {{ item.type }}; // {{ item.comment }}{% endfor %}pageNum: number; // 當前頁碼pageSize: number; // 每頁條數};};
}const edit{{table_info.firstUpperName}}Ref = ref();
const state = reactive<TableDataState>({ids: [],tableData: {data: [],total: 0,loading: false,param: {{% for item in front_info.serach_columns %}{% if item.type == 'string' %}{{ item.camelName }}: '',{% else %}{{ item.camelName }}: 0,{% endif %}{% endfor %}pageNum: 1,pageSize: 10,},},
});// 初始化表格數據
const initTableData = () => {{{ table_info.camelName }}List();
};// 查詢{{ table_info.comment }}列表數據
const {{ table_info.camelName }}List = () => {get{{ table_info.firstUpperName }}List(state.tableData.param).then(res => {state.tableData.data = res.data.{{ table_info.camelName }}List ?? [];state.tableData.total = res.data.total;});
};// 打開新增{{ table_info.comment }}彈窗
const onOpenAdd{{ table_info.firstUpperName }} = () => {edit{{ table_info.firstUpperName }}Ref.value.openDialog();
};// 打開修改{{ table_info.comment }}彈窗
const onOpenEdit{{ table_info.firstUpperName }} = (row: Object) => {row = Object.assign({}, row);edit{{ table_info.firstUpperName }}Ref.value.openDialog(row);
};// 刪除{{ table_info.comment }}
const onRowDel = (row: any) => {let msg = '你確定要刪除所選{{ table_info.comment }}?';let ids: number[] = [];if (row) {msg = `此操作將永久刪除{{ table_info.comment }}:“${row.{{ front_info.primarkName }}}”,是否繼續?`;ids = [row.id];} else {ids = state.ids;}if (ids.length === 0) {ElMessage.error('請選擇要刪除的數據。');return;}ElMessageBox.confirm(msg, '提示', {confirmButtonText: '確認',cancelButtonText: '取消',type: 'warning',}).then(() => {batchDelete{{ table_info.firstUpperName }}(ids).then(() => {ElMessage.success('刪除成功');{{ table_info.camelName }}List();});}).catch(() => {});
};// 分頁改變
const onHandleSizeChange = (val: number) => {state.tableData.param.pageSize = val;{{ table_info.camelName }}List();
};// 分頁改變
const onHandleCurrentChange = (val: number) => {state.tableData.param.pageNum = val;{{ table_info.camelName }}List();
};// 多選框選中數據
const handleSelectionChange = (selection: Array<TableData>) => {state.ids = selection.map(item => item.id);
};// 頁面加載時
onMounted(() => {initTableData();
});
</script>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83907.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83907.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83907.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

鴻蒙組件通用屬性深度解析:從基礎樣式到高級定制的全場景指南

一、引言&#xff1a;通用屬性 —— 構建視覺體驗的核心語言 在鴻蒙應用開發體系中&#xff0c;組件的視覺呈現與交互體驗主要通過通用屬性系統實現。這些屬性構成了從基礎樣式定義&#xff08;尺寸、顏色&#xff09;到復雜交互控制&#xff08;動畫、布局&#xff09;的完整…

選擇與方法專欄(9) 職場內篇: 是否要跳出舒適圈?如何處理犯錯?

合集文章 一個中科大差生的8年程序員工作總結_8年工作經驗 程序員-CSDN博客 選擇與方法專欄&#xff08;1&#xff09;職場外篇&#xff1a;謹慎的選擇城市、行業、崗位-CSDN博客 選擇與方法專欄&#xff08;2&#xff09;職場外篇&#xff1a; 每個時代都有自己的機遇-CSDN…

DCM4CHEE ARCHIVE LIGHT 源代碼解析(1)-前言

系列文章目錄 DCM4CHEE ARCHIVE LIGHT 源代碼解析(1)-前言DCM4CHEE ARCHIVE LIGHT 源代碼解析(2)-STOWRS文章目錄 系列文章目錄概述一、項目結構1、下載解壓代碼2、IntelliJ IDEA加載源代碼二、編譯發布1、編譯 dcm4chee-arc-ear 項目2、編譯 dcm4chee-arc-ui2 項目寫在結尾概…

基于DeepSeek-R1-Distill-Llama-8B的健康管理助手微調過程

基于DeepSeek-R1-Distill-Llama-8B的健康管理助手微調過程 本次創新實訓項目的主要任務是利用DEEPSEEK提供的開源模型&#xff0c;通過微調技術&#xff0c;實現一個專注于健康管理與醫療咨詢的人工智能助手。本文詳細記錄我們如何對DeepSeek-R1-Distill-Llama-8B模型進行微調…

TI 毫米波雷達走讀系列—— 3DFFT及測角

TI 毫米波雷達走讀系列—— 3DFFT及測角 測角原理 —— 角度怎么測測角公式 —— 角度怎么算相位差測角基本公式為什么是3DFFT1. 空間頻率與角度的對應關系2. FFT的數學本質&#xff1a;離散空間傅里葉變換 測角原理 —— 角度怎么測 本節內容解決角度怎么測的問題&#xff0c…

圖解JavaScript原型:原型鏈及其分析 02 | JavaScript圖解

? ? 任何函數既可以看成一個實例對象又可以看成一個函數 作為一個實例對象其隱式原型對象指向其構造函數的顯式原型對象 作為一個函數其顯式原型對象指向一個空對象 任何一個函數其隱式原型對象指向其構造函數的顯式原型對象 任何一個函數是 Function 函數創建的實例&…

自定義View實現K歌開始前歌詞上方圓點倒計時動畫效果

在上一篇KRC歌詞解析原理及Android實現K歌動態歌詞效果介紹了動態歌詞的實現,我們繼續完善一下。在K歌場景中,一些歌曲前奏很長,用戶不知道什么時候開始唱,這時一般在歌詞上方會有一個圓點倒計時的效果來提醒用戶開始時間,如下圖:開始唱之前,圓點會逐個減少,直至圓點全…

ffmpeg subtitles 字幕不換行的問題解決方案

使用ffmpeg在mac下處理srt中文字幕可以自動換行&#xff0c;linux環境下不換行直接超出視頻區域了 這是因為在mac環境下的SimpleText 渲染器自動處理了文本&#xff0c;而linux無法處理。 mac&#xff1a; linux&#xff1a; 方案&#xff1a; ?&#xff1a;網上找到的方案…

Trino入門:開啟分布式SQL查詢新世界

目錄 一、Trino 是什么 二、核心概念與架構解析 2.1 關鍵概念詳解 2.2 架構剖析 三、快速上手之安裝部署 3.1 環境準備 3.2 安裝步驟 3.2.1 下載軟件包 3.2.2 安裝軟件包 3.2.3 啟動服務 3.2.4 驗證服務 3.2.5 安裝 Trino 客戶端 3.3 目錄結構說明 四、實戰演練&…

EFK架構的數據安全性

EFK架構&#xff08;Elasticsearch Filebeat Kibana&#xff09;的數據安全性需通過?傳輸加密、訪問控制、存儲保護?三層措施保障&#xff0c;其核心風險與加固方案如下&#xff1a; 一、數據傳輸安全風險與加固 ?明文傳輸風險? Filebeat → Elasticsearch 的日…

2025年滲透測試面試題總結-安全服務工程師(駐場)(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 安全服務工程師(駐場) 1. 挖過的一些漏洞&#xff08;舉例說明&#xff09; 2. 滲透測試的思路&#xff08…

C++ 編譯流程詳解:從源碼到可執行文件

C 編譯流程是將人類可讀的源代碼轉換為計算機可執行的二進制文件的過程。這個過程可以分為四個核心階段&#xff1a;預處理、編譯、匯編和鏈接。每個階段都有明確的任務&#xff0c;共同確保代碼正確轉換為可執行程序。 一、預處理&#xff08;Preprocessing&#xff09; 預處…

CentOS7 安裝最新版 Docker

在 CentOS 7 上安裝最新版 Docker&#xff0c;可以按照以下步驟操作&#xff1a; 1. 卸載舊版本 Docker&#xff08;如有&#xff09; 如果之前安裝過舊版 Docker&#xff0c;需要先卸載&#xff1a; yum remove docker docker-client docker-client-latest docker-common do…

網絡安全相關知識

一、網絡安全基礎 1. CIA三元組 (Confidentiality, Integrity, Availability) 機密性 (Confidentiality)&#xff1a;確保信息只能由授權人員查看&#xff0c;防止信息泄露。加密技術&#xff08;如AES、RSA&#xff09;通常用于保護機密性。 完整性 (Integrity)&#xff1a;…

每天一個前端小知識 Day 4 - TypeScript 核心類型系統與實踐

TypeScript 核心類型系統與實踐 1. 為什么前端面試中越來越重視 TypeScript&#xff1f; 復雜業務場景需要強類型保障穩定性&#xff1b;更好的 IDE 支持和智能提示&#xff1b;降低線上 bug 概率&#xff1b;成熟的工程團隊都在使用&#xff1b;對于 React/Vue 項目維護可讀性…

uni-app插件,高德地圖、地圖區域繪制、定位打卡

介紹 高德地圖、地圖區域繪制、定位打卡 示例 默認 &#xff08;展示地圖&#xff0c;是否可以打卡&#xff09; <template><view class"container"><map-positioning-punch:clock-in-area"clockInArea":refresh-timeout"refreshT…

_mm_aeskeygenassist_si128 硬件指令執行的操作

根據Intel的文檔&#xff0c;_mm_aeskeygenassist_si128 指令執行以下操作&#xff1a; result[31:0] SubWord(RotWord(temp)) xor Rcon; result[63:32] SubWord(RotWord(temp)); result[95:64] SubWord(RotWord(temp)) xor Rcon; result[127:96] SubWord(RotWord(temp…

爬蟲技術:數據獲取的利器與倫理邊界

一、爬蟲技術的原理與架構 爬蟲技術的核心是模擬人類瀏覽網頁的行為&#xff0c;通過程序自動訪問網站&#xff0c;獲取網頁內容。其基本原理可以分為以下幾個步驟&#xff1a;首先&#xff0c;爬蟲程序會發送一個 HTTP 請求到目標網站的服務器&#xff0c;請求獲取網頁數據。…

TortoiseSVN 下載指定版本客戶端及對應翻譯(漢化)包

訪問官方網站 打開瀏覽器,進入 TortoiseSVN 官方網站:https://tortoisesvn.net/ ,這是獲取官方版本最權威的渠道。 進入下載頁面 在官網首頁,找到并點擊 Downloads(下載)選項,進入下載頁面。 選擇版本 在下載頁面中,會展示最新版本的下載鏈接。如果需要指定版本,向下…

MacOS15.5 MySQL8 開啟 mysql_native_password

MySQL 8 默認關閉了 mysql_native_password&#xff0c; 需要手動開啟。但是MacOS各種坑&#xff0c;氣死個人。 mysql8 內置了 mysql_native_password &#xff0c;只是沒有開啟。 驗證方式是執行 show plugins; &#xff0c;返回的結果中應該有 mysql_native_password &…