Tagtag Starter 框架全面支持 AI 辅助开发,通过集成 Model Context Protocol (MCP) 和提供 AI 友好的文档,让您能够充分利用 AI 工具来加速开发流程。
Model Context Protocol (MCP) 是一个开放标准,用于将 AI 助手(如 Claude、ChatGPT 等)连接到外部数据源和工具。通过 MCP,AI 可以直接访问您的项目文档、代码库和其他资源,从而提供更准确、更相关的帮助。
Tagtag Starter 官方文档已经集成了 nuxt-llms 模块,自动生成 AI 可读的文档文件:
访问地址:
这些文档遵循 llms.txt 标准,可以被各种 AI 工具和 MCP 服务器直接读取和解析。
Tagtag Starter 文档站点已经配置了 @nuxtjs/mcp-toolkit,提供了标准的 MCP 工具接口,允许 AI 助手直接访问和查询文档内容。
在 Trae 中,您需要配置 MCP 服务器来连接到 Tagtag Starter 的文档资源。
创建或编辑您的 MCP 配置文件(通常在用户配置目录):
{
"mcpServers": {
"tagtag-docs": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"http://tagtag.dev/llms.txt"
],
"env": {}
}
}
}
配置完成后,您就可以在 Trae 中使用 AI 助手了:
// 示例:询问 AI 如何创建新的 CRUD 模块
"请帮我创建一个用户管理模块,包含增删改查功能"
AI 会自动访问 Tagtag Starter 文档,理解框架结构,并为您提供准确的代码示例和指导。
// 向 AI 提问
"基于 Tagtag Starter 框架,帮我生成一个产品管理的 CRUD 模块,包括:
- 实体类 Product
- Mapper 接口
- Service 层
- Controller 层
- 前端页面"
AI 会参考 Tagtag Starter 的代码规范和最佳实践,生成符合框架标准的代码。
步骤 1:生成实体类
// 向 AI 提问
"生成 Product 实体类,包含以下字段:
- id (Long, 主键)
- name (String, 产品名称,非空)
- code (String, 产品编码,唯一)
- price (BigDecimal, 价格,非空)
- stock (Integer, 库存数量)
- status (Integer, 状态:0-禁用,1-启用)
- description (String, 产品描述)
- createTime (LocalDateTime, 创建时间)
- updateTime (LocalDateTime, 更新时间)
- createBy (String, 创建人)
- updateBy (String, 更新人)
- deleted (Integer, 逻辑删除标记)
参考 Tagtag Starter 的 User 实体类实现"
AI 生成的代码:
package dev.tagtag.module.product.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("sys_product")
public class Product {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String code;
private BigDecimal price;
private Integer stock;
private Integer status;
private String description;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private String createBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@TableLogic
private Integer deleted;
}
步骤 2:生成 DTO 类
// 向 AI 提问
"生成 ProductDTO 和 ProductQuery 类,用于数据传输和查询
参考 Tagtag Starter 的 UserDTO 和 UserQuery 实现"
AI 生成的代码:
package dev.tagtag.module.product.dto;
import lombok.Data;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
@Data
public class ProductDTO {
private Long id;
@NotBlank(message = "产品名称不能为空")
private String name;
@NotBlank(message = "产品编码不能为空")
private String code;
@NotNull(message = "价格不能为空")
@DecimalMin(value = "0.01", message = "价格必须大于 0")
private BigDecimal price;
@NotNull(message = "库存数量不能为空")
@Min(value = 0, message = "库存数量不能为负数")
private Integer stock;
@NotNull(message = "状态不能为空")
private Integer status;
private String description;
}
package dev.tagtag.module.product.dto;
import lombok.Data;
@Data
public class ProductQuery {
private String name;
private String code;
private Integer status;
private Integer pageNum = 1;
private Integer pageSize = 10;
}
步骤 3:生成 Mapper 接口
// 向 AI 提问
"生成 ProductMapper 接口,继承 BaseMapper
参考 Tagtag Starter 的 UserMapper 实现"
AI 生成的代码:
package dev.tagtag.module.product.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import dev.tagtag.module.product.entity.Product;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
步骤 4:生成 Service 层
// 向 AI 提问
"生成 ProductService 接口和 ProductServiceImpl 实现类
包含以下方法:
- pageProducts(ProductQuery query): 分页查询产品
- getProductById(Long id): 根据 ID 查询产品
- createProduct(ProductDTO productDTO): 创建产品
- updateProduct(ProductDTO productDTO): 更新产品
- deleteProduct(Long id): 删除产品
- batchDeleteProducts(List<Long> ids): 批量删除产品
- updateProductStatus(Long id, Integer status): 更新产品状态
参考 Tagtag Starter 的 UserService 实现"
AI 生成的代码:
package dev.tagtag.module.product.service;
import dev.tagtag.module.product.dto.ProductDTO;
import dev.tagtag.module.product.dto.ProductQuery;
import dev.tagtag.common.core.page.PageResult;
public interface ProductService {
PageResult<ProductDTO> pageProducts(ProductQuery query);
ProductDTO getProductById(Long id);
void createProduct(ProductDTO productDTO);
void updateProduct(ProductDTO productDTO);
void deleteProduct(Long id);
void batchDeleteProducts(java.util.List<Long> ids);
void updateProductStatus(Long id, Integer status);
}
package dev.tagtag.module.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import dev.tagtag.module.product.dto.ProductDTO;
import dev.tagtag.module.product.dto.ProductQuery;
import dev.tagtag.module.product.entity.Product;
import dev.tagtag.module.product.mapper.ProductMapper;
import dev.tagtag.module.product.service.ProductService;
import dev.tagtag.common.core.page.PageResult;
import dev.tagtag.common.core.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class ProductServiceImpl implements ProductService {
private final ProductMapper productMapper;
@Override
public PageResult<ProductDTO> pageProducts(ProductQuery query) {
LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
wrapper.like(query.getName() != null, Product::getName, query.getName())
.eq(query.getCode() != null, Product::getCode, query.getCode())
.eq(query.getStatus() != null, Product::getStatus, query.getStatus())
.orderByDesc(Product::getCreateTime);
Page<Product> page = productMapper.selectPage(
new Page<>(query.getPageNum(), query.getPageSize()),
wrapper
);
return PageResult.of(page, this::convertToDTO);
}
@Override
public ProductDTO getProductById(Long id) {
Product product = productMapper.selectById(id);
if (product == null) {
throw new BusinessException("产品不存在");
}
return convertToDTO(product);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void createProduct(ProductDTO productDTO) {
Product product = convertToEntity(productDTO);
productMapper.insert(product);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProduct(ProductDTO productDTO) {
Product product = productMapper.selectById(productDTO.getId());
if (product == null) {
throw new BusinessException("产品不存在");
}
BeanUtils.copyProperties(productDTO, product);
productMapper.updateById(product);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteProduct(Long id) {
productMapper.deleteById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void batchDeleteProducts(java.util.List<Long> ids) {
ids.forEach(this::deleteProduct);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProductStatus(Long id, Integer status) {
Product product = productMapper.selectById(id);
if (product == null) {
throw new BusinessException("产品不存在");
}
product.setStatus(status);
productMapper.updateById(product);
}
private ProductDTO convertToDTO(Product product) {
ProductDTO dto = new ProductDTO();
BeanUtils.copyProperties(product, dto);
return dto;
}
private Product convertToEntity(ProductDTO dto) {
Product product = new Product();
BeanUtils.copyProperties(dto, product);
return product;
}
}
步骤 5:生成 Controller 层
// 向 AI 提问
"生成 ProductController,提供 RESTful API
参考 Tagtag Starter 的 UserController 实现"
AI 生成的代码:
package dev.tagtag.module.product.controller;
import dev.tagtag.common.core.page.PageResult;
import dev.tagtag.common.core.result.Result;
import dev.tagtag.module.product.dto.ProductDTO;
import dev.tagtag.module.product.dto.ProductQuery;
import dev.tagtag.module.product.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@GetMapping
public Result<PageResult<ProductDTO>> pageProducts(ProductQuery query) {
PageResult<ProductDTO> result = productService.pageProducts(query);
return Result.success(result);
}
@GetMapping("/{id}")
public Result<ProductDTO> getProductById(@PathVariable Long id) {
ProductDTO product = productService.getProductById(id);
return Result.success(product);
}
@PostMapping
public Result<Void> createProduct(@RequestBody ProductDTO productDTO) {
productService.createProduct(productDTO);
return Result.success();
}
@PutMapping
public Result<Void> updateProduct(@RequestBody ProductDTO productDTO) {
productService.updateProduct(productDTO);
return Result.success();
}
@DeleteMapping("/{id}")
public Result<Void> deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
return Result.success();
}
@DeleteMapping("/batch")
public Result<Void> batchDeleteProducts(@RequestBody java.util.List<Long> ids) {
productService.batchDeleteProducts(ids);
return Result.success();
}
@PutMapping("/{id}/status")
public Result<Void> updateProductStatus(@PathVariable Long id, @RequestParam Integer status) {
productService.updateProductStatus(id, status);
return Result.success();
}
}
步骤 6:生成前端页面
// 向 AI 提问
"生成产品管理的前端页面,包括:
- 产品列表页面(index.vue)
- 产品表单组件(FormModal.vue)
参考 Tagtag Starter 的用户管理页面实现
使用 Vue 3 Composition API、TypeScript、VXE Table"
AI 生成的代码:
<template>
<div class="product-container">
<Grid
ref="gridRef"
:columns="columns"
:data="products"
:toolbar-config="toolbarConfig"
:checkbox-config="{ checkField: 'checked' }"
@toolbar-click="handleToolbarClick"
>
<template #toolbar_buttons>
<a-button type="primary" @click="handleAdd">
<template #icon><PlusOutlined /></template>
新增
</a-button>
<a-button
:disabled="!hasSelection"
@click="handleBatchDelete"
>
<template #icon><DeleteOutlined /></template>
批量删除
</a-button>
</template>
<template #action="{ row }">
<a-space>
<a-button type="link" @click="handleEdit(row)">编辑</a-button>
<a-button type="link" danger @click="handleDelete(row)">删除</a-button>
<a-switch
:checked="row.status === 1"
@change="(checked) => handleStatusChange(row, checked)"
/>
</a-space>
</template>
</Grid>
<FormModal
v-model:visible="modalVisible"
:title="modalTitle"
:form-data="formData"
@submit="handleSubmit"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import { useTable } from '@vben/plugins/vxe-table';
import {
pageProducts,
createProduct,
updateProduct,
deleteProduct,
batchDeleteProducts,
updateProductStatus
} from '@/api/product';
import type { ProductDTO, ProductQuery } from '@/api/product';
const [Grid, gridApi] = useTable();
const columns = [
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: '产品名称', minWidth: 150 },
{ field: 'code', title: '产品编码', width: 120 },
{ field: 'price', title: '价格', width: 100, formatter: ({ cellValue }) => `¥${cellValue}` },
{ field: 'stock', title: '库存', width: 80 },
{ field: 'status', title: '状态', width: 80, formatter: ({ cellValue }) => cellValue === 1 ? '启用' : '禁用' },
{ field: 'createTime', title: '创建时间', width: 160 },
{ title: '操作', slots: { default: 'action' }, width: 200, fixed: 'right' }
];
const products = ref<ProductDTO[]>([]);
const modalVisible = ref(false);
const modalTitle = ref('');
const formData = ref<Partial<ProductDTO>>({});
const toolbarConfig = {
buttons: [
{ code: 'add', name: '新增' }
]
};
const hasSelection = computed(() => {
return gridApi.value?.getSelectedRows().length > 0;
});
const loadData = async () => {
const query: ProductQuery = { pageNum: 1, pageSize: 10 };
const result = await pageProducts(query);
products.value = result.records;
};
const handleAdd = () => {
modalTitle.value = '新增产品';
formData.value = {};
modalVisible.value = true;
};
const handleEdit = (row: ProductDTO) => {
modalTitle.value = '编辑产品';
formData.value = { ...row };
modalVisible.value = true;
};
const handleDelete = (row: ProductDTO) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除产品"${row.name}"吗?`,
onOk: async () => {
await deleteProduct(row.id!);
message.success('删除成功');
loadData();
}
});
};
const handleBatchDelete = async () => {
const selectedRows = gridApi.value?.getSelectedRows() || [];
Modal.confirm({
title: '确认删除',
content: `确定要删除选中的 ${selectedRows.length} 个产品吗?`,
onOk: async () => {
const ids = selectedRows.map(row => row.id);
await batchDeleteProducts(ids);
message.success('批量删除成功');
loadData();
}
});
};
const handleStatusChange = async (row: ProductDTO, checked: boolean) => {
await updateProductStatus(row.id!, checked ? 1 : 0);
message.success('状态更新成功');
loadData();
};
const handleSubmit = async (data: ProductDTO) => {
if (data.id) {
await updateProduct(data);
message.success('更新成功');
} else {
await createProduct(data);
message.success('创建成功');
}
modalVisible.value = false;
loadData();
};
loadData();
</script>
<template>
<a-modal
:visible="visible"
:title="title"
:confirm-loading="loading"
@ok="handleSubmit"
@cancel="handleCancel"
>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="产品名称" name="name">
<a-input v-model:value="formState.name" placeholder="请输入产品名称" />
</a-form-item>
<a-form-item label="产品编码" name="code">
<a-input v-model:value="formState.code" placeholder="请输入产品编码" />
</a-form-item>
<a-form-item label="价格" name="price">
<a-input-number
v-model:value="formState.price"
:min="0.01"
:precision="2"
style="width: 100%"
placeholder="请输入价格"
/>
</a-form-item>
<a-form-item label="库存数量" name="stock">
<a-input-number
v-model:value="formState.stock"
:min="0"
style="width: 100%"
placeholder="请输入库存数量"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="formState.status">
<a-radio :value="1">启用</a-radio>
<a-radio :value="0">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="产品描述" name="description">
<a-textarea
v-model:value="formState.description"
:rows="4"
placeholder="请输入产品描述"
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import { ref, watch, reactive } from 'vue';
import type { FormInstance } from 'ant-design-vue';
import type { ProductDTO } from '@/api/product';
interface Props {
visible: boolean;
title: string;
formData: Partial<ProductDTO>;
}
interface Emits {
(e: 'update:visible', value: boolean): void;
(e: 'submit', data: ProductDTO): void;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
const formRef = ref<FormInstance>();
const loading = ref(false);
const formState = reactive<Partial<ProductDTO>>({});
const rules = {
name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
stock: [{ required: true, message: '请输入库存数量', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
};
watch(() => props.formData, (data) => {
Object.assign(formState, data);
}, { immediate: true });
const handleSubmit = async () => {
try {
loading.value = true;
await formRef.value?.validate();
emit('submit', formState as ProductDTO);
} catch (error) {
console.error('表单验证失败:', error);
} finally {
loading.value = false;
}
};
const handleCancel = () => {
emit('update:visible', false);
formRef.value?.resetFields();
};
</script>
// 向 AI 提问
"Tagtag Starter 中的 RBAC 权限系统是如何实现的?"
AI 会从文档中提取相关信息,为您提供详细的解释和代码示例。
// 向 AI 提问
"请详细解释 Tagtag Starter 中的 RBAC 权限系统,包括:
1. 权限模型设计(用户、角色、权限的关系)
2. 数据库表结构
3. 权限验证的实现方式
4. 前端权限控制
5. 后端权限注解使用
请提供相关代码示例"
AI 提供的详细解答:
1. 权限模型设计
Tagtag Starter 采用基于 RBAC(Role-Based Access Control)的权限模型,包含以下核心概念:
2. 数据库表结构
-- 用户表
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
real_name VARCHAR(50),
email VARCHAR(100),
phone VARCHAR(20),
status TINYINT DEFAULT 1,
create_time DATETIME,
update_time DATETIME,
create_by VARCHAR(50),
update_by VARCHAR(50),
deleted TINYINT DEFAULT 0
);
-- 角色表
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL,
role_code VARCHAR(50) NOT NULL UNIQUE,
description VARCHAR(200),
status TINYINT DEFAULT 1,
create_time DATETIME,
update_time DATETIME,
create_by VARCHAR(50),
update_by VARCHAR(50),
deleted TINYINT DEFAULT 0
);
-- 权限表
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
permission_name VARCHAR(50) NOT NULL,
permission_code VARCHAR(100) NOT NULL UNIQUE,
resource_type VARCHAR(20),
resource_url VARCHAR(200),
description VARCHAR(200),
create_time DATETIME,
update_time DATETIME,
create_by VARCHAR(50),
update_by VARCHAR(50),
deleted TINYINT DEFAULT 0
);
-- 菜单表
CREATE TABLE sys_menu (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
menu_name VARCHAR(50) NOT NULL,
menu_code VARCHAR(50) NOT NULL UNIQUE,
parent_id BIGINT DEFAULT 0,
menu_type VARCHAR(20),
icon VARCHAR(50),
path VARCHAR(200),
component VARCHAR(200),
sort_order INT DEFAULT 0,
status TINYINT DEFAULT 1,
create_time DATETIME,
update_time DATETIME,
create_by VARCHAR(50),
update_by VARCHAR(50),
deleted TINYINT DEFAULT 0
);
-- 用户角色关联表
CREATE TABLE sys_user_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
create_time DATETIME,
UNIQUE KEY uk_user_role (user_id, role_id)
);
-- 角色权限关联表
CREATE TABLE sys_role_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
create_time DATETIME,
UNIQUE KEY uk_role_permission (role_id, permission_id)
);
-- 角色菜单关联表
CREATE TABLE sys_role_menu (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_id BIGINT NOT NULL,
menu_id BIGINT NOT NULL,
create_time DATETIME,
UNIQUE KEY uk_role_menu (role_id, menu_id)
);
3. 权限验证的实现方式
// 权限验证注解
package dev.tagtag.common.security.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequiresPermissions {
String[] value() default {};
Logical logical() default Logical.AND;
enum Logical {
AND, OR
}
}
// 权限验证切面
package dev.tagtag.common.security.aspect;
import dev.tagtag.common.security.annotation.RequiresPermissions;
import dev.tagtag.common.security.service.PermissionService;
import dev.tagtag.common.core.exception.ForbiddenException;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
@Aspect
@Component
@RequiredArgsConstructor
public class PermissionAspect {
private final PermissionService permissionService;
@Before("@annotation(dev.tagtag.common.security.annotation.RequiresPermissions)")
public void checkPermission(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
RequiresPermissions requiresPermissions = signature.getMethod()
.getAnnotation(RequiresPermissions.class);
String[] permissions = requiresPermissions.value();
RequiresPermissions.Logical logical = requiresPermissions.logical();
boolean hasPermission = permissionService.hasPermissions(permissions, logical);
if (!hasPermission) {
throw new ForbiddenException("权限不足");
}
}
}
// 权限服务
package dev.tagtag.common.security.service;
import dev.tagtag.common.security.utils.SecurityUtils;
import dev.tagtag.module.iam.mapper.UserMapper;
import dev.tagtag.module.iam.mapper.RoleMapper;
import dev.tagtag.module.iam.mapper.PermissionMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PermissionService {
private final UserMapper userMapper;
private final RoleMapper roleMapper;
private final PermissionMapper permissionMapper;
public boolean hasPermissions(String[] permissions, RequiresPermissions.Logical logical) {
Long userId = SecurityUtils.getUserId();
Set<String> userPermissions = getUserPermissions(userId);
if (logical == RequiresPermissions.Logical.AND) {
return userPermissions.containsAll(List.of(permissions));
} else {
return List.of(permissions).stream()
.anyMatch(userPermissions::contains);
}
}
private Set<String> getUserPermissions(Long userId) {
List<Long> roleIds = userMapper.getRoleIdsByUserId(userId);
return roleIds.stream()
.flatMap(roleId -> permissionMapper.getPermissionCodesByRoleId(roleId).stream())
.collect(Collectors.toSet());
}
}
4. 前端权限控制
// 权限指令
import type { Directive, DirectiveBinding } from 'vue';
const permission: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding;
const permissions = useUserStore().permissions;
if (value && !permissions.includes(value)) {
el.parentNode?.removeChild(el);
}
}
};
export default permission;
// 权限组合式函数
import { computed } from 'vue';
import { useUserStore } from '@/store/user';
export function usePermission() {
const userStore = useUserStore();
const hasPermission = (permission: string | string[]): boolean => {
const permissions = userStore.permissions;
if (Array.isArray(permission)) {
return permission.some(p => permissions.includes(p));
}
return permissions.includes(permission);
};
const hasAllPermissions = (permissions: string[]): boolean => {
const userPermissions = userStore.permissions;
return permissions.every(p => userPermissions.includes(p));
};
const hasAnyPermission = (permissions: string[]): boolean => {
const userPermissions = userStore.permissions;
return permissions.some(p => userPermissions.includes(p));
};
return {
hasPermission,
hasAllPermissions,
hasAnyPermission
};
}
// 使用示例
<template>
<div>
<!-- 使用指令 -->
<a-button v-permission="'user:create'">新增用户</a-button>
<!-- 使用组合式函数 -->
<a-button v-if="hasPermission('user:update')">编辑</a-button>
<!-- 批量权限检查 -->
<a-button v-if="hasAllPermissions(['user:delete', 'user:export'])">
批量操作
</a-button>
</div>
</template>
<script setup lang="ts">
import { usePermission } from '@/hooks/usePermission';
const { hasPermission, hasAllPermissions } = usePermission();
</script>
5. 后端权限注解使用
// Controller 中使用权限注解
package dev.tagtag.module.iam.controller;
import dev.tagtag.common.security.annotation.RequiresPermissions;
import dev.tagtag.common.core.result.Result;
import dev.tagtag.module.iam.dto.UserDTO;
import dev.tagtag.module.iam.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
@RequiresPermissions("user:list")
public Result<PageResult<UserDTO>> pageUsers(UserQuery query) {
PageResult<UserDTO> result = userService.pageUsers(query);
return Result.success(result);
}
@GetMapping("/{id}")
@RequiresPermissions("user:query")
public Result<UserDTO> getUserById(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
return Result.success(user);
}
@PostMapping
@RequiresPermissions("user:create")
public Result<Void> createUser(@RequestBody UserDTO userDTO) {
userService.createUser(userDTO);
return Result.success();
}
@PutMapping
@RequiresPermissions("user:update")
public Result<Void> updateUser(@RequestBody UserDTO userDTO) {
userService.updateUser(userDTO);
return Result.success();
}
@DeleteMapping("/{id}")
@RequiresPermissions("user:delete")
public Result<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success();
}
@PutMapping("/{id}/status")
@RequiresPermissions(value = {"user:update", "user:status"}, logical = Logical.OR)
public Result<Void> updateUserStatus(@PathVariable Long id, @RequestParam Integer status) {
userService.updateUserStatus(id, status);
return Result.success();
}
}
// 向 AI 提问
"请审查这段代码,看是否符合 Tagtag Starter 的开发规范"
AI 会根据 Tagtag 的代码风格指南,提供改进建议。
// 向 AI 提问
"我在使用 Tagtag Starter 的文件存储功能时遇到了问题,错误信息是..."
AI 会结合文档和常见问题,帮助您快速定位和解决问题。
当向 AI 提问时,提供足够的上下文信息:
// 好的提问
"在 Tagtag Starter 框架中,我需要创建一个新的模块 'order',包含订单管理功能。
请参考现有的 'user' 模块结构,生成相应的代码。"
// 不好的提问
"帮我写个订单模块"
对于复杂的任务,将其分解为多个小步骤:
// 第一步
"帮我创建订单实体类 Order,包含 id、userId、amount、status 等字段"
// 第二步
"基于 Order 实体,创建对应的 Mapper 接口"
// 第三步
"创建 OrderService 接口和实现类"
AI 生成的代码需要人工审查和测试:
通过 AI 辅助开发,您可以:
如果您需要扩展 MCP 功能,可以在 Tagtag Starter 项目中添加自定义工具:
// server/mcp/tools/custom-tools.ts
export default defineMCPTool({
name: 'tagtag-code-generator',
description: 'Generate Tagtag Starter module code',
inputSchema: {
type: 'object',
properties: {
moduleName: {
type: 'string',
description: 'Module name'
}
}
},
handler: async (input) => {
// 自定义代码生成逻辑
return {
success: true,
data: 'Generated code...'
}
}
})
将 AI 辅助开发集成到您的 CI/CD 流程中:
# .github/workflows/ai-review.yml
name: AI Code Review
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: AI Review
run: |
npx @modelcontextprotocol/cli review \
--docs http://tagtag.dev/llms.txt
A: 不建议直接使用。AI 生成的代码需要经过人工审查、测试和优化,确保符合项目规范和业务需求。
A: 提供清晰、具体的上下文信息,参考 Tagtag Starter 的文档结构,使用框架术语,这样 AI 能提供更准确的回答。
A: 检查网络连接,确认文档地址可访问,查看 MCP 服务器日志,确保配置正确。
A: 可以。您可以下载 llms.txt 文件到本地,配置本地 MCP 服务器,实现离线访问。
Tagtag Starter 框架通过以下方式支持 AI 辅助开发:
通过合理使用 AI 辅助开发,您可以:
开始使用 AI 辅助开发,让 Tagtag Starter 成为您开发路上的得力助手!
需要帮助?欢迎在 GitHub Issues 中提问。