Markdown Converter
Agent skill for markdown-converter
This project scaffolds API layers (service, biz, data) from OpenAPI descriptions. When working under the repository, follow the
Sign in to like and favorite skills
This project scaffolds API layers (service, biz, data) from OpenAPI descriptions. When working under the repository, follow the principles below so regenerated code and manual extensions continue to compose cleanly.
internal/api/service)Responsibility: Transport layer handlers and request/response translation
resp envelope formatresp.SuccessJSON, resp.ListDataResponse, resp.OneDataResponse, resp.OperateSuccess to ensure all success responses conform to { "msg": "string", "code": 0, "data": { ... } } formatCorrect Example:
func (c *UserController) CreateUser(ctx echo.Context) error { var req param.UserCreateRequest if err := BindAndValidate(ctx, &req); err != nil { return err // Return directly, handled by ErrorHandler middleware } bizCtx := utils.BuildContext(ctx) err := c.user.Create(bizCtx, req) if err != nil { return err // Return directly, handled by ErrorHandler middleware } return resp.OperateSuccess(ctx) // Use unified response format } func (c *UserController) ListUsers(ctx echo.Context) error { var req param.UserListUsersRequest if err := BindAndValidate(ctx, &req); err != nil { return err } bizCtx := utils.BuildContext(ctx) list, total, err := c.user.ListUsers(bizCtx, req) if err != nil { return err } return resp.ListDataResponse(ctx, list, total) // Use list response format }
Incorrect Example:
// ❌ Wrong: Direct database operations in Service layer func (c *UserController) CreateUser(ctx echo.Context) error { var user model.User if err := c.db.Create(&user).Error; err != nil { return err } return ctx.JSON(200, user) // ❌ Wrong: Not using unified response format } // ❌ Wrong: Business logic processing in Service layer func (c *UserController) CreateUser(ctx echo.Context) error { var req param.UserCreateRequest if err := BindAndValidate(ctx, &req); err != nil { return err } // ❌ Wrong: Business logic should be in biz layer if req.Age < 18 { return errors.New("age must be greater than 18") } return c.user.Create(ctx, req) }
internal/api/biz)Responsibility: Encapsulate domain workflows and business invariants while staying storage-agnostic
code packageDataManagerCorrect Example:
type UserHandler struct { repo UserRepository // Inject through interface, not direct DataManager dependency } func NewUserHandler(repo UserRepository) UserUseCase { return &UserHandler{repo: repo} } func (h *UserHandler) CreateUser(ctx context.Context, req param.UserCreateRequest) error { // Business rule validation if err := h.validateUserData(req); err != nil { return code.WrapValidationError(err, "user data validation failed") } // Call Repository layer err := h.repo.Create(ctx, req) if err != nil { return code.WrapDatabaseError(err, "create user failed") } return nil } // Pure function validation helper func (h *UserHandler) validateUserData(req param.UserCreateRequest) error { if req.Age < 18 { return errors.New("age must be greater than 18") } return nil }
Incorrect Example:
// ❌ Wrong: Direct dependency on DataManager type UserHandler struct { dm *data.DataManager // ❌ Wrong: Should inject through Repository interface } func (h *UserHandler) CreateUser(ctx context.Context, req param.UserCreateRequest) error { // ❌ Wrong: Direct database operations in Biz layer var user model.User if err := h.dm.MySQLWithContext(ctx).Create(&user).Error; err != nil { return err // ❌ Wrong: Not using code package to wrap errors } return nil }
internal/api/data)Responsibility: Implement concrete persistence adapters and external integrations
code package to wrap database errors, provide meaningful error messagesCorrect Example:
type userRepository struct { d *db.DataManager } func NewUserRepository(d *db.DataManager) biz.UserRepository { return userRepository{d: d} } func (u userRepository) Create(ctx context.Context, req param.UserCreateRequest) error { user := model.User{ Username: req.Username, Email: req.Email, Age: int32(req.Age), CreatedAt: time.Now(), UpdatedAt: time.Now(), } err := u.d.Query.User.WithContext(ctx).Create(&user) if err != nil { return code.WrapDatabaseError(err, "create user failed") } return nil } func (u userRepository) ListUsers(ctx context.Context, req param.UserListUsersRequest) ([]param.UserListItem, int64, error) { users, err := u.d.Query.User.WithContext(ctx).Offset(req.Offset()).Limit(req.Limit()).Find() if err != nil { return nil, 0, code.WrapDatabaseError(err, "query user list failed") } var list []param.UserListItem for _, user := range users { list = append(list, param.UserListItem{ Id: user.ID, Username: user.Username, Email: user.Email, Age: int(user.Age), CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, }) } count, err := u.d.Query.User.Count() if err != nil { return nil, 0, code.WrapDatabaseError(err, "query user count failed") } return list, count, nil }
Incorrect Example:
// ❌ Wrong: Business logic processing in Data layer func (u userRepository) Create(ctx context.Context, req param.UserCreateRequest) error { // ❌ Wrong: Business logic should be in biz layer if req.Age < 18 { return errors.New("age must be greater than 18") } // Database operations... } // ❌ Wrong: Not wrapping database errors func (u userRepository) Create(ctx context.Context, req param.UserCreateRequest) error { err := u.d.Query.User.WithContext(ctx).Create(&user) if err != nil { return err // ❌ Wrong: Should use code package to wrap } return nil }
Correct Example:
// Define Repository interface type UserRepository interface { Create(ctx context.Context, req param.UserCreateRequest) error GetByID(ctx context.Context, id int64) (param.UserData, error) ListUsers(ctx context.Context, req param.UserListUsersRequest) ([]param.UserListItem, int64, error) Update(ctx context.Context, id int64, req param.UserUpdateRequest) error Delete(ctx context.Context, id int64) error } // Inject through interface in Biz layer type UserHandler struct { repo UserRepository }
internal/code package. These errors will be translated by the global ErrorHandler middlewareresp package methods to ensure { "msg": "string", "code": 0, "data": { ... } } contractError Code Usage Example:
// Define error codes in code package const ( ErrUserNotFound = 10001 ErrUserAlreadyExists = 10002 ) // Use in Data layer func (u userRepository) GetByID(ctx context.Context, id int64) (param.UserData, error) { user, err := u.d.Query.User.WithContext(ctx).GetByID(uint(id)) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return param.UserData{}, code.WrapNotFoundError(err, "user not found") } return param.UserData{}, code.WrapDatabaseError(err, "query user failed") } return convertToUserData(user), nil }
Use the unified response methods provided by
resp package:
// List data response return resp.ListDataResponse(ctx, list, total) // Single data response return resp.OneDataResponse(ctx, data) // Operation success response return resp.OperateSuccess(ctx) // Custom success response return resp.SuccessJSON(ctx, map[string]interface{}{ "message": "operation successful", "data": result, })
internal/resp, internal/code, etc.)ctx context.Context) for operations that may need cancellation or tracingService layer templates must:
resp package methods for all success responsesBindAndValidate for parameter binding and validationBiz layer templates must:
code package to wrap all errorsDataManagerData layer templates must:
code package to wrap database errorsAll API responses must use the following format:
{ "msg": "operation successful", "code": 0, "data": { // actual data } }
internal/code package to define error codescode.WrapXXXError in Data layer to wrap errorscode.WrapXXXError in Biz layer to wrap Repository errorsif, t.Helper, etc.)go test ./... (or a targeted subset) before submitting changes; include any non-standard flags used in the test outputWhen reviewing generated code, ensure:
code packageresp package methods