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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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 &…