first commit
This commit is contained in:
185
.cursor/rules/api.mdc
Normal file
185
.cursor/rules/api.mdc
Normal file
@@ -0,0 +1,185 @@
|
||||
# Cursor 工作流程和规范
|
||||
|
||||
## 目录结构说明
|
||||
|
||||
### API 定义目录 (`app/main/api/desc/`)
|
||||
```
|
||||
desc/
|
||||
├── admin/ # 后台管理接口
|
||||
│ ├── admin_user.api # 管理员用户相关接口
|
||||
│ ├── platform_user.api # 平台用户相关接口
|
||||
│ ├── order.api # 订单管理接口
|
||||
│ ├── menu.api # 菜单管理接口
|
||||
│ ├── role.api # 角色权限接口
|
||||
│ └── auth.api # 后台认证接口
|
||||
│
|
||||
├── front/ # 前台业务接口
|
||||
│ ├── user.api # 用户相关接口
|
||||
│ ├── product.api # 产品相关接口
|
||||
│ ├── pay.api # 支付相关接口
|
||||
│ ├── query.api # 查询相关接口
|
||||
│ ├── auth/ # 前台认证相关子模块
|
||||
│ ├── product/ # 产品相关子模块
|
||||
│ ├── query/ # 查询相关子模块
|
||||
│ ├── user/ # 用户相关子模块
|
||||
│ └── pay/ # 支付相关子模块
|
||||
│
|
||||
└── main.api # API 主入口文件,用于引入所有子模块
|
||||
```
|
||||
|
||||
### 目录规范
|
||||
1. 后台管理接口统一放在 `admin/` 目录下
|
||||
- 所有后台管理相关的 API 定义文件都放在此目录
|
||||
- 文件名应清晰表明模块功能,如 `order.api`、`user.api` 等
|
||||
- 后台接口统一使用 `/api/v1/admin/` 前缀
|
||||
|
||||
2. 前台业务接口统一放在 `front/` 目录下
|
||||
- 所有面向用户的 API 定义文件都放在此目录
|
||||
- 可以根据业务模块创建子目录,如 `user/`、`product/` 等
|
||||
- 前台接口统一使用 `/api/v1/` 前缀
|
||||
|
||||
3. 主入口文件 `main.api`
|
||||
- 用于引入所有子模块的 API 定义
|
||||
- 保持清晰的模块分类和注释
|
||||
|
||||
## API 开发流程
|
||||
|
||||
### 1. API 定义
|
||||
- 根据业务类型选择正确的目录:
|
||||
- 后台管理接口:`app/main/api/desc/admin/` 目录
|
||||
- 前台业务接口:`app/main/api/desc/front/` 目录
|
||||
- 创建新的 `.api` 文件,遵循以下命名规范:
|
||||
- 后台接口:`[模块名].api`,如 `order.api`、`user.api`
|
||||
- 前台接口:`[模块名].api`,如 `product.api`、`pay.api`
|
||||
- API 定义规范:
|
||||
- 使用 RESTful 风格
|
||||
- 请求/响应结构体命名规范:`[模块名][操作名][Req/Resp]`
|
||||
- 字段类型使用 string 而不是 int64 来存储枚举值
|
||||
- 添加必要的注释说明
|
||||
- 请求/响应参数定义规范:
|
||||
```go
|
||||
type (
|
||||
// 创建类请求(必填字段)
|
||||
AdminCreateXXXReq {
|
||||
Field1 string `json:"field1"` // 字段1说明
|
||||
Field2 int64 `json:"field2"` // 字段2说明
|
||||
Field3 string `json:"field3"` // 字段3说明
|
||||
}
|
||||
|
||||
// 更新类请求(可选字段使用指针类型)
|
||||
AdminUpdateXXXReq {
|
||||
Id int64 `path:"id"` // ID(路径参数)
|
||||
Field1 *string `json:"field1,optional"` // 字段1说明(可选)
|
||||
Field2 *int64 `json:"field2,optional"` // 字段2说明(可选)
|
||||
Field3 *string `json:"field3,optional"` // 字段3说明(可选)
|
||||
}
|
||||
|
||||
// 查询列表请求(分页参数 + 可选查询条件)
|
||||
AdminGetXXXListReq {
|
||||
Page int64 `form:"page"` // 页码
|
||||
PageSize int64 `form:"pageSize"` // 每页数量
|
||||
Field1 *string `form:"field1,optional"` // 查询条件1(可选)
|
||||
Field2 *int64 `form:"field2,optional"` // 查询条件2(可选)
|
||||
Field3 *string `form:"field3,optional"` // 查询条件3(可选)
|
||||
}
|
||||
|
||||
// 列表项结构体(用于列表响应)
|
||||
XXXListItem {
|
||||
Id int64 `json:"id"` // ID
|
||||
Field1 string `json:"field1"` // 字段1
|
||||
Field2 string `json:"field2"` // 字段2
|
||||
Field3 string `json:"field3"` // 字段3
|
||||
CreateTime string `json:"create_time"` // 创建时间
|
||||
UpdateTime string `json:"update_time"` // 更新时间
|
||||
}
|
||||
|
||||
// 列表响应
|
||||
AdminGetXXXListResp {
|
||||
Total int64 `json:"total"` // 总数
|
||||
Items []XXXListItem `json:"items"` // 列表数据
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
- 参数定义注意事项:
|
||||
1. 创建类请求(Create):
|
||||
- 所有字段都是必填的,使用普通类型(非指针)
|
||||
- 使用 `json` 标签,不需要 `optional` 标记
|
||||
- 必须添加字段说明注释
|
||||
|
||||
2. 更新类请求(Update):
|
||||
- 除 ID 外的所有字段都是可选的,使用指针类型(如 `*string`、`*int64`)
|
||||
- 使用 `json` 标签,并添加 `optional` 标记
|
||||
- ID 字段使用 `path` 标签,因为是路径参数
|
||||
- 必须添加字段说明注释
|
||||
|
||||
3. 查询列表请求(GetList):
|
||||
- 分页参数(page、pageSize)使用普通类型
|
||||
- 查询条件字段都是可选的,使用指针类型
|
||||
- 使用 `form` 标签,并添加 `optional` 标记
|
||||
- 必须添加字段说明注释
|
||||
|
||||
4. 响应结构体:
|
||||
- 列表响应使用 `Total` 和 `Items` 字段
|
||||
- 列表项使用单独的结构体定义
|
||||
- 时间字段统一使用 string 类型
|
||||
- 必须添加字段说明注释
|
||||
|
||||
5. 标签使用规范:
|
||||
- 路径参数:`path:"id"`
|
||||
- JSON 参数:`json:"field_name"`
|
||||
- 表单参数:`form:"field_name"`
|
||||
- 可选字段:添加 `optional` 标记
|
||||
- 所有字段必须添加说明注释
|
||||
|
||||
- 示例:
|
||||
```go
|
||||
// 通知管理接口
|
||||
@server(
|
||||
prefix: /api/v1/admin/notification
|
||||
group: admin_notification
|
||||
)
|
||||
service main {
|
||||
// 创建通知
|
||||
@handler AdminCreateNotification
|
||||
post /create (AdminCreateNotificationReq) returns (AdminCreateNotificationResp)
|
||||
|
||||
// 更新通知
|
||||
@handler AdminUpdateNotification
|
||||
put /update/:id (AdminUpdateNotificationReq) returns (AdminUpdateNotificationResp)
|
||||
|
||||
// 获取通知列表
|
||||
@handler AdminGetNotificationList
|
||||
get /list (AdminGetNotificationListReq) returns (AdminGetNotificationListResp)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 引入 API
|
||||
- 在 `app/main/api/main.api` 中引入新定义的 API 文件:
|
||||
```go
|
||||
import "desc/order.api"
|
||||
```
|
||||
|
||||
### 3. 生成代码
|
||||
- 在项目根目录运行 `gen_api.ps1` 脚本生成相关代码:
|
||||
```powershell
|
||||
./gen_api.ps1
|
||||
```
|
||||
- 生成的文件包括:
|
||||
- `app/main/api/internal/handler/` - 处理器
|
||||
- `app/main/api/internal/logic/` - 业务逻辑
|
||||
- `app/main/api/internal/svc/` - 服务上下文
|
||||
- `app/main/api/internal/types/` - 类型定义
|
||||
|
||||
### 4. 实现业务逻辑
|
||||
- 在 `app/main/api/internal/logic/` 目录下实现各个接口的业务逻辑
|
||||
- 具体实现规范请参考 `logic.mdc` 文件
|
||||
|
||||
## 注意事项
|
||||
1. 所有枚举类型使用 string 而不是 int64
|
||||
2. 错误处理必须使用 errors.Wrapf 和 xerr 包
|
||||
3. 涉及多表操作时使用事务
|
||||
4. 并发操作时注意使用互斥锁保护共享资源
|
||||
5. 时间类型统一使用 "2006-01-02 15:04:05" 格式
|
||||
6. 代码生成后需要检查并完善业务逻辑实现
|
||||
7. 保持代码风格统一,添加必要的注释
|
||||
270
.cursor/rules/logic.mdc
Normal file
270
.cursor/rules/logic.mdc
Normal file
@@ -0,0 +1,270 @@
|
||||
# Your rule content
|
||||
|
||||
- You can @ files here
|
||||
- You can use markdown but dont have to
|
||||
|
||||
# Logic 实现规范
|
||||
|
||||
## 目录结构
|
||||
```
|
||||
app/main/api/internal/logic/
|
||||
├── admin_notification/ # 通知管理模块
|
||||
│ ├── admincreatenotificationlogic.go # 创建通知
|
||||
│ ├── admindeletnotificationlogic.go # 删除通知
|
||||
│ ├── admingetnotificationdetaillogic.go # 获取通知详情
|
||||
│ ├── admingetnotificationlistlogic.go # 获取通知列表
|
||||
│ └── adminupdatenotificationlogic.go # 更新通知
|
||||
└── [其他模块]/
|
||||
```
|
||||
|
||||
## Logic 实现规范
|
||||
|
||||
### 1. 基础结构
|
||||
每个 Logic 文件都应包含以下基础结构:
|
||||
```go
|
||||
package [模块名]
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/common/xerr"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type [操作名]Logic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func New[操作名]Logic(ctx context.Context, svcCtx *svc.ServiceContext) *[操作名]Logic {
|
||||
return &[操作名]Logic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 增删改查实现规范
|
||||
|
||||
#### 2.1 创建操作(Create)
|
||||
```go
|
||||
func (l *[操作名]Logic) [操作名](req *types.[操作名]Req) (resp *types.[操作名]Resp, err error) {
|
||||
// 1. 数据转换和验证
|
||||
data := &model.[表名]{
|
||||
Field1: req.Field1,
|
||||
Field2: req.Field2,
|
||||
// ... 其他字段映射
|
||||
}
|
||||
|
||||
// 2. 数据库操作
|
||||
result, err := l.svcCtx.[表名]Model.Insert(l.ctx, nil, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"创建[操作对象]失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 3. 返回结果
|
||||
id, _ := result.LastInsertId()
|
||||
return &types.[操作名]Resp{Id: id}, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 删除操作(Delete)
|
||||
```go
|
||||
func (l *[操作名]Logic) [操作名](req *types.[操作名]Req) (resp *types.[操作名]Resp, err error) {
|
||||
// 1. 查询记录是否存在
|
||||
record, err := l.svcCtx.[表名]Model.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找[操作对象]失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 执行删除操作(软删除)
|
||||
err = l.svcCtx.[表名]Model.DeleteSoft(l.ctx, nil, record)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"删除[操作对象]失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 3. 返回结果
|
||||
return &types.[操作名]Resp{Success: true}, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 更新操作(Update)
|
||||
```go
|
||||
func (l *[操作名]Logic) [操作名](req *types.[操作名]Req) (resp *types.[操作名]Resp, err error) {
|
||||
// 1. 查询记录是否存在
|
||||
record, err := l.svcCtx.[表名]Model.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找[操作对象]失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 更新字段(使用指针判断是否更新)
|
||||
if req.Field1 != nil {
|
||||
record.Field1 = *req.Field1
|
||||
}
|
||||
if req.Field2 != nil {
|
||||
record.Field2 = *req.Field2
|
||||
}
|
||||
// ... 其他字段更新
|
||||
|
||||
// 3. 执行更新操作
|
||||
_, err = l.svcCtx.[表名]Model.Update(l.ctx, nil, record)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"更新[操作对象]失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 4. 返回结果
|
||||
return &types.[操作名]Resp{Success: true}, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.4 查询详情(GetDetail)
|
||||
```go
|
||||
func (l *[操作名]Logic) [操作名](req *types.[操作名]Req) (resp *types.[操作名]Resp, err error) {
|
||||
// 1. 查询记录
|
||||
record, err := l.svcCtx.[表名]Model.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查找[操作对象]失败, err: %v, id: %d", err, req.Id)
|
||||
}
|
||||
|
||||
// 2. 构建响应
|
||||
resp = &types.[操作名]Resp{
|
||||
Id: record.Id,
|
||||
Field1: record.Field1,
|
||||
Field2: record.Field2,
|
||||
CreateTime: record.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: record.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
// 3. 处理可选字段(如时间字段)
|
||||
if record.OptionalField.Valid {
|
||||
resp.OptionalField = record.OptionalField.Time.Format("2006-01-02")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.5 查询列表(GetList)
|
||||
```go
|
||||
func (l *[操作名]Logic) [操作名](req *types.[操作名]Req) (resp *types.[操作名]Resp, err error) {
|
||||
// 1. 构建查询条件
|
||||
builder := l.svcCtx.[表名]Model.SelectBuilder()
|
||||
|
||||
// 2. 添加查询条件(使用指针判断是否添加条件)
|
||||
if req.Field1 != nil {
|
||||
builder = builder.Where("field1 LIKE ?", "%"+*req.Field1+"%")
|
||||
}
|
||||
if req.Field2 != nil {
|
||||
builder = builder.Where("field2 = ?", *req.Field2)
|
||||
}
|
||||
// ... 其他查询条件
|
||||
|
||||
// 3. 执行分页查询
|
||||
list, total, err := l.svcCtx.[表名]Model.FindPageListByPageWithTotal(
|
||||
l.ctx, builder, req.Page, req.PageSize, "id DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
|
||||
"查询[操作对象]列表失败, err: %v, req: %+v", err, req)
|
||||
}
|
||||
|
||||
// 4. 构建响应列表
|
||||
items := make([]types.[列表项类型], 0, len(list))
|
||||
for _, item := range list {
|
||||
listItem := types.[列表项类型]{
|
||||
Id: item.Id,
|
||||
Field1: item.Field1,
|
||||
Field2: item.Field2,
|
||||
CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: item.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
// 处理可选字段
|
||||
if item.OptionalField.Valid {
|
||||
listItem.OptionalField = item.OptionalField.Time.Format("2006-01-02")
|
||||
}
|
||||
items = append(items, listItem)
|
||||
}
|
||||
|
||||
// 5. 返回结果
|
||||
return &types.[操作名]Resp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 错误处理规范
|
||||
1. 使用 `errors.Wrapf` 包装错误
|
||||
2. 使用 `xerr.NewErrCode` 创建业务错误
|
||||
3. 错误信息应包含:
|
||||
- 操作类型(创建/更新/删除/查询)
|
||||
- 具体错误描述
|
||||
- 相关参数信息(ID、请求参数等)
|
||||
|
||||
### 4. 时间处理规范
|
||||
1. 时间格式化:
|
||||
- 日期时间:`"2006-01-02 15:04:05"`
|
||||
- 仅日期:`"2006-01-02"`
|
||||
2. 可选时间字段处理:
|
||||
```go
|
||||
if field.Valid {
|
||||
resp.Field = field.Time.Format("2006-01-02")
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 数据库操作规范
|
||||
1. 查询条件构建:
|
||||
```go
|
||||
builder := l.svcCtx.[表名]Model.SelectBuilder()
|
||||
builder = builder.Where("field = ?", value)
|
||||
```
|
||||
2. 分页查询:
|
||||
```go
|
||||
list, total, err := l.svcCtx.[表名]Model.FindPageListByPageWithTotal(
|
||||
ctx, builder, page, pageSize, "id DESC")
|
||||
```
|
||||
3. 事务处理:
|
||||
```go
|
||||
err = l.svcCtx.[表名]Model.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 事务操作
|
||||
return nil
|
||||
})
|
||||
```
|
||||
|
||||
### 6. 并发处理规范
|
||||
```go
|
||||
var mu sync.Mutex
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
// 并发处理
|
||||
}, func(item interface{}, writer mr.Writer[struct{}], cancel func(error)) {
|
||||
// 处理单个项目
|
||||
}, func(pipe <-chan struct{}, cancel func(error)) {
|
||||
// 完成处理
|
||||
})
|
||||
```
|
||||
|
||||
### 7. 注意事项
|
||||
1. 所有数据库操作必须进行错误处理
|
||||
2. 更新操作必须使用指针类型判断字段是否更新
|
||||
3. 查询列表必须支持分页
|
||||
4. 时间字段必须统一格式化
|
||||
5. 可选字段必须进行空值判断
|
||||
6. 保持代码风格统一,添加必要的注释
|
||||
7. 涉及多表操作时使用事务
|
||||
8. 并发操作时注意使用互斥锁保护共享资源
|
||||
9. 错误处理必须使用 errors.Wrapf 和 xerr 包
|
||||
10. 时间类型统一使用 "2006-01-02 15:04:05" 格式
|
||||
|
||||
# Your rule content
|
||||
|
||||
- You can @ files here
|
||||
- You can use markdown but dont have to
|
||||
Reference in New Issue
Block a user