适用项目:
opms_backend/opms_parent目标:让任意 AI 在无额外教学前提下,直接产出与现有代码风格、分层习惯、业务约束一致的新功能代码。
1.16(见 go.mod,禁止使用高于 1.16 的语法特性,如泛型)github.com/gogf/gf v1.16.x(禁止使用 v2.x 版本特性,如 gf-cli 新命令、核心 API 变更)rpcx(服务注册、调用必须遵循项目现有注册格式,见 main.go 示例)opms_libary/micro_srv(必须通过 svc.Init(ctx) 获取租户信息,禁止手动解析 token)opms_libary/myerrors(禁止使用原生 error 直接返回,必须用 myerrors 封装)service.CreateSystemMessage),消息参数必须包含租户 ID、操作用户 IDexcelize(导出文件命名格式:模块名_操作_时间戳.xlsx,如 work_order_export_202604221530.xlsx)src/utils/request.js 和 src/utils/micro_request.js)本项目采用 微服务 RPCX 架构,前端调用后端接口必须遵循以下规范:
| 场景 | 使用模块 | 请求方式 | 说明 |
|---|---|---|---|
| 业务接口(主流程) | micro_request.js |
RPCX POST | 所有业务功能接口必须使用此方式 |
| 文件上传/下载 | request.js 或原生 axios |
HTTP | 大文件传输等特殊场景 |
| 基础/公共接口 | request.js |
REST | 登录、字典等公共服务 |
请求 URL 格式:
POST http://localhost:11000/micro-srv-proxy/{servicePath}
请求 Headers:
Content-Type: application/rpcx
X-RPCX-SerializeType: 1
X-RPCX-ServicePath: {ServiceName} // 如:DeliveryProject、DeliveryProjectEvent
X-RPCX-ServiceMethod: {MethodName} // 如:GetList、Create、UpdateById
SrvEnv: dev // 开发环境标识
前端 API 文件模板:
import micro_request from '@/utils/micro_request'
const basePath = process.env.VUE_APP_ParentPath // 微服务路径,如:dashoo.opms.parent-0.0.1
export default {
// 获取列表
getList(params) {
return micro_request.postRequest(basePath, 'ServiceName', 'GetList', params)
},
// 创建
create(data) {
return micro_request.postRequest(basePath, 'ServiceName', 'Create', data)
},
// 更新
update(data) {
return micro_request.postRequest(basePath, 'ServiceName', 'UpdateById', data)
},
// 删除
deleteByIds(ids) {
return micro_request.postRequest(basePath, 'ServiceName', 'DeleteByIds', { ids })
}
}
❌ 错误示例(REST 方式):
// 不要这样写!
import request from '@/utils/request'
const baseUrl = '/api/v1/deliveryProject' // 错误:使用了 REST 路径
export default {
getList(params) {
return request({
url: `${baseUrl}/list`, // 错误:生成了 /api/v1/deliveryProject/list
method: 'get',
params,
})
}
}
✅ 正确示例(RPCX 方式):
// 应该这样写!
import micro_request from '@/utils/micro_request'
const basePath = process.env.VUE_APP_ParentPath
export default {
getList(params) {
return micro_request.postRequest(basePath, 'DeliveryProject', 'GetList', params)
}
}
micro_request:所有业务接口统一使用 micro_request.postRequest 方法main.go 中 s.RegisterName("Xxx", ...) 的名称一致{ code: 200, data: {...}, msg: "" },通过 res.data 获取数据1.16(见 go.mod,禁止使用高于 1.16 的语法特性,如泛型)github.com/gogf/gf v1.16.x(禁止使用 v2.x 版本特性,如 gf-cli 新命令、核心 API 变更)rpcx(服务注册、调用必须遵循项目现有注册格式,见 main.go 示例)opms_libary/micro_srv(必须通过 svc.Init(ctx) 获取租户信息,禁止手动解析 token)opms_libary/myerrors(禁止使用原生 error 直接返回,必须用 myerrors 封装)service.CreateSystemMessage),消息参数必须包含租户 ID、操作用户 IDexcelize(导出文件命名格式:模块名_操作_时间戳.xlsx,如 work_order_export_202604221530.xlsx)app/handler/*:接口层(仅做 3 件事:接收请求参数、简单参数校验、调用 service、封装 rsp.Data,禁止写任何业务逻辑)app/service/*:业务层(核心职责:业务规则校验、事务控制、审批流程调用、系统消息发送、业务动态日志写入)app/dao/*:数据访问层(GF DAO 规范,仅做数据 CRUD,禁止写业务判断;含 internal 生成代码 + 外层封装,外层封装仅做简单查询拼接)app/model/*:模型层(仅定义请求/响应 DTO、数据库实体映射,禁止写方法、禁止引入业务依赖)main.go:RPC 服务注册入口(必须通过 s.RegisterName(...) 注册 handler,注册顺序与现有模块保持一致)svc.Init(ctx),确保拿到 Tenant/CxtUser/DataScope,未初始化禁止操作 DAOservice.SetCreatedInfo,更新用 service.SetUpdatedInfo,缺一不可FieldsEx(service.UpdateFieldEx...),service.UpdateFieldEx 为全局统一不可改字段集合(如 id、created_at、created_by 等)myerrors.TipsError("中文提示"),系统内部错误用 myerrors.DbError("") / myerrors.SystemError("")g.Map{"list": list, "total": total},禁止返回数组、单个结构体或其他格式work_order.go、ctr_contract.go;模块目录名全小写,如 work、projXxxHandler / Xxx(与模块名对应,如工单模块:WorkOrderHandler)xxxService 或 XxxService(统一前缀为模块名,如 workService、WorkOrderService)XxxReq、XxxSearchReq、XxxUpdateReq、XxxRsp(Req 对应请求,Rsp 对应响应,SearchReq 对应列表查询请求)Xxx(与表名对应,首字母大写,如 WorkOrder 对应表 work_order)GetList(入参为 SearchReq,返回 total、list、err)Create(入参为 AddReq / CreateReq,返回 err)UpdateById(入参为 UpdateReq,必须包含 Id,返回 err)DeleteById(入参为 int 类型 Id,返回 err)DeleteByIds(入参为 []int64 类型 Ids,返回 err)GetById(入参为 int 类型 Id,返回实体、err)"10"/"20"/"30"),禁止随意改成 iota;新增状态需在对应 model 中定义常量,如 const (WorkOrderStatusTodo = "10" WorkOrderStatusDoing = "20")userName、workOrderList,禁止单字母命名(除 ctx、err、tx 等通用缩写)// Swagger:模块名 中文模块 中文动作,如 // Swagger:work 工单 分页查询// 这里是循环、// 删除数据),注释必须解释“为什么这么做”,而非“做了什么”// XxxHandler 工单接口处理类、// Create 新增工单,包含事务控制和动态日志if err != nil { return err } 早返回风格,禁止嵌套 if-err,禁止忽略 errdata := new(model.Xxx)、var t g.Map、list := make([]T, 0),禁止使用 var data model.Xxx 后手动赋值零值db/dao,按请求参数逐项 Where(无参数则不写),最后按“Count + Page + Order + Scan”顺序执行,禁止打乱顺序Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {...}),事务内所有 DAO 操作必须使用 TX(tx),禁止混合使用非事务 DAOg.Log().Error(err);用户可见错误必须使用 myerrors.* 封装,禁止直接返回 err 给前端dao 与 model 引用必须使用别名,别名格式为“模块名+dao/model”,如:
xxxdao "dashoo.cn/opms/app/dao/<module>"(如 workdao "dashoo.cn/opms/app/dao/work")xxxmodel "dashoo.cn/opms/app/model/<module>"(如 workmodel "dashoo.cn/opms/app/model/work").As("x")(如 .As("a"))时,后续 Where 条件才可写 x.<字段名>;若未设置 .As("x"),禁止使用 x.<字段名> 前缀结构体转 Map 写法:将请求结构体转为 g.Map 用于 DAO 写入时,必须使用 gconv.Map(req),禁止使用 gconv.Struct(req, &g.Map{}) 或 gconv.Struct(req, data)(其中 data 为 g.Map 类型)。GoFrame 的 gconv.Struct 仅支持将数据转为结构体指针,不支持 g.Map 作为目标,传入 g.Map 会在运行时报错 params should be type of pointer, but got type: map。正确写法:
// ✅ 正确:使用 gconv.Map 将结构体转为 map
data := gconv.Map(req)
// ❌ 错误:gconv.Struct 不支持 g.Map 作为目标
data := g.Map{}
gconv.Struct(req, &data) // 运行时报错!
gconv.Map 的 key 格式与 DAO Columns 的冲突:gconv.Map(req) 产生的 key 是 JSON tag 的小驼峰格式(如 eventNo、feedbackDate),而 s.Dao.Columns.Xxx 返回的是数据库列名的蛇形格式(如 event_no、feedback_date)。在 gconv.Map 产生的 data map 中,必须使用小驼峰 key 赋值或判断,禁止使用 s.Dao.Columns.Xxx 作为 key。正确写法:
data := gconv.Map(req)
data["eventNo"] = generateEventNo() // ✅ 正确:使用小驼峰 key
data[s.Dao.Columns.EventNo] = "xxx" // ❌ 错误:Columns.EventNo 是 "event_no",与 map 中的 "eventNo" 是不同的 key
if v, ok := data["feedbackDate"]; ok {} // ✅ 正确
if v, ok := data[s.Dao.Columns.FeedbackDate]; ok {} // ❌ 错误:查找 "feedback_date" key,但 map 中只有 "feedbackDate"
注意:直接构造 g.Map{...} 时(非 gconv.Map 产生),key 应使用 s.Dao.Columns.Xxx(蛇形格式),因为手动构造的 map 不经过 JSON tag 转换。
注意:当目标是模型结构体时,gconv.Struct(req, &modelEntity) 是正确的用法,不可混淆。
代码缩进:使用 4 个空格缩进,禁止使用 tab;大括号换行规范:左括号紧跟语句后,右括号单独成行,如:
if err != nil {
return err
}
新增一个业务对象时,最少包含以下文件,文件路径、命名必须严格匹配:
app/model/<module>/<entity>.go:定义请求/响应结构、实体映射(如 app/model/work/work_order.go)app/service/<module>/<entity>.go:业务逻辑实现,包含所有常规方法(GetList、Create 等)(如 app/service/work/work_order.go)app/handler/<module>/<entity>.go:接口入口,实现 handler 方法,调用对应 service(如 app/handler/work/work_order.go)app/dao/<module>/... + app/model/<module>/internal/...(通过 GF工具生成,禁止手动修改结构)main.go:在 RPC 服务注册区,添加该模块 handler 的注册代码(s.RegisterName)rsp.Data 后返回NewXxxService(ctx) 初始化,获取租户、用户上下文,未初始化成功禁止后续操作workflowSrv.StartProcessInstance)、发送系统消息(如 service.CreateSystemMessage)workflowSrv.StartProcessInstance 启动审批流程 -> 回写 appro_type(审批类型)、appro_instance_id(审批实例 ID)到业务表bizCode(业务编码)、ProcessType(流程类型)、Result(审批结果),校验失败直接返回错误,不执行后续操作ctx = context.WithValue(ctx, "contextService", s)dao := s.Dao.DataScope(ctx, "incharge_id").As("a")s.CxtUser.Roles 判断角色,增补数据可见范围(如管理员可见所有数据、普通用户仅可见自己创建的数据)典型模式(必须严格沿用):
// 状态校验
currentStatus := data.Status
if currentStatus != "10" {
return myerrors.TipsError("当前状态不可执行该操作")
}
// 合法则执行更新
return s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
// 更新状态
_, err := s.Dao.TX(tx).WherePri(data.Id).Data(g.Map{"status": "20"}).Update()
if err != nil {
return err
}
// 写动态日志
// 发送通知(如有)
return nil
})
SetCreatedInfo/SetUpdatedInfo,新增/更新操作必须补齐审计字段myerrors.TipsError 封装中文提示app/dao/**/internal、app/model/**/internal 自动生成文件的结构语义,仅可通过 GF 工具重新生成更新myerrors 封装后返回app/model/<module>/xxx.go)package <module> // 替换为实际模块名,如work、proj
import (
"dashoo.cn/opms_libary/request"
"github.com/gogf/gf/frame/g"
)
// XxxSearchReq 列表查询请求
type XxxSearchReq struct {
request.PageReq // 必须嵌入分页请求,统一分页参数
Name string `json:"name" v:"-"` // 非必填参数,v:"-"表示不校验
Status string `json:"status" v:"-"` // 状态参数,沿用字符串枚举
// 新增其他查询参数,按实际业务补充,字段名小驼峰,注释说明含义
}
// XxxAddReq 新增请求(必填项需加v校验)
type XxxAddReq struct {
Name string `json:"name" v:"required#名称不能为空"` // 必填项,中文校验提示
Status string `json:"status" v:"required#状态不能为空"`
// 新增其他新增参数,按实际业务补充
}
// XxxUpdateReq 更新请求(必须包含Id,必填)
type XxxUpdateReq struct {
Id int `json:"id" v:"required#ID不能为空"` // 必须包含Id,必填校验
Name string `json:"name" v:"-"` // 非必填,可选择性更新
Status string `json:"status" v:"-"`
// 新增其他更新参数,按实际业务补充
}
// Xxx 数据库实体映射(与表名对应)
type Xxx struct {
Id int `json:"id" orm:"id,pk,autoincr"` // 主键,自增
Name string `json:"name" orm:"name"`
Status string `json:"status" orm:"status"`
TenantId int `json:"tenantId" orm:"tenant_id"` // 租户ID,必须包含
CreatedAt string `json:"createdAt" orm:"created_at"` // 审计字段,自动填充
CreatedBy int `json:"createdBy" orm:"created_by"`
CreatedName string `json:"createdName" orm:"created_name"`
UpdatedAt string `json:"updatedAt" orm:"updated_at"`
UpdatedBy int `json:"updatedBy" orm:"updated_by"`
UpdatedName string `json:"updatedName" orm:"updated_name"`
}
// XxxRsp 响应结构体(给前端返回的结构,按需筛选字段)
type XxxRsp struct {
Id int `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
// 新增其他需要返回的字段,按实际业务补充
}
app/service/<module>/xxx.go)package <module> // 替换为实际模块名,如work、proj
import (
"context"
xxxdao "dashoo.cn/opms/app/dao/<module>" // 替换为实际dao别名,如workdao
xxxmodel "dashoo.cn/opms/app/model/<module>" // 替换为实际model别名,如workmodel
"dashoo.cn/opms/app/service"
"dashoo.cn/opms_libary/myerrors"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
)
// xxxService 业务逻辑实现类(替换xxx为模块名,如workService)
type xxxService struct {
*service.ContextService
Dao *xxxdao.XxxDao // 替换Xxx为实体名,如WorkOrderDao
}
// NewXxxService 初始化service(替换Xxx为模块名,如NewWorkService)
func NewXxxService(ctx context.Context) (svc *xxxService, err error) {
svc = new(xxxService)
// 初始化上下文,获取租户、用户信息
if svc.ContextService, err = svc.Init(ctx); err != nil {
return nil, err
}
// 初始化DAO,关联租户
svc.Dao = xxxdao.NewXxxDao(svc.Tenant) // 替换Xxx为实体名,如NewWorkOrderDao
return svc, nil
}
// GetList 分页查询(固定方法名,与handler保持一致)
func (s *xxxService) GetList(req *xxxmodel.XxxSearchReq) (total int, list []*xxxmodel.XxxRsp, err error) {
// 构造DAO查询
db := s.Dao.As("a")
// 拼接查询条件(按需补充,无条件则删除对应if)
if req.Name != "" {
db = db.WhereLike("a."+s.Dao.C.Name, "%"+req.Name+"%")
}
if req.Status != "" {
db = db.Where("a."+s.Dao.C.Status, req.Status)
}
// 数据权限过滤(必须添加,无特殊情况不可删除)
db = service.DataScope(s.Ctx, db, "a.tenant_id")
// 统计总行数
total, err = db.Count()
if err != nil {
g.Log().Error(err)
return 0, nil, myerrors.DbError("获取XXX总行数失败") // 替换XXX为业务名称,如工单
}
// 分页、排序、查询
var entityList []*xxxmodel.Xxx
err = db.Page(req.GetPage()).OrderDesc("a.id").Scan(&entityList)
if err != nil {
g.Log().Error(err)
return 0, nil, myerrors.DbError("查询XXX列表失败")
}
// 转换为响应结构体(按需转换,若无需转换可直接返回entityList)
if err = gconv.Structs(entityList, &list); err != nil {
g.Log().Error(err)
return 0, nil, myerrors.SystemError("数据转换失败")
}
return
}
// Create 新增(固定方法名,与handler保持一致)
func (s *xxxService) Create(req *xxxmodel.XxxAddReq) error {
// 转换请求参数为实体
data := new(xxxmodel.Xxx)
if err := gconv.Struct(req, data); err != nil {
g.Log().Error(err)
return myerrors.SystemError("数据转换失败")
}
// 补齐审计字段(必须添加,不可删除)
service.SetCreatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
// 关联租户ID(必须添加,不可删除)
data.TenantId = s.Tenant.Id
// 事务控制(新增操作必须加事务)
return s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
// 新增数据
_, err := s.Dao.TX(tx).Insert(data)
if err != nil {
g.Log().Error(err)
return myerrors.DbError("新增XXX失败")
}
// TODO: 写业务动态日志(必须实现,不可删除)
// 示例:service.CreateDynamic(...)
// TODO: 触发审批流/发送消息(按需补充)
return nil
})
}
// UpdateById 根据ID更新(固定方法名,与handler保持一致)
func (s *xxxService) UpdateById(req *xxxmodel.XxxUpdateReq) error {
// 校验数据是否存在
count, err := s.Dao.WherePri(req.Id).Count()
if err != nil {
g.Log().Error(err)
return myerrors.DbError("查询XXX数据失败")
}
if count == 0 {
return myerrors.TipsError("XXX数据不存在")
}
// 构造更新数据
data := g.Map{
"name": req.Name,
"status": req.Status,
// 补充其他需要更新的字段
}
// 补齐审计字段(必须添加,不可删除)
service.SetUpdatedInfo(data, s.GetCxtUserId(), s.GetCxtUserName())
// 更新操作,排除不可改字段(必须添加FieldsEx)
_, err = s.Dao.FieldsEx(service.UpdateFieldEx...).WherePri(req.Id).Data(data).Update()
if err != nil {
g.Log().Error(err)
return myerrors.DbError("更新XXX失败")
}
// TODO: 写业务动态日志(必须实现,不可删除)
// TODO: 触发审批流/发送消息(按需补充)
return nil
}
// DeleteById 根据ID单条删除(固定方法名,与handler保持一致)
func (s *xxxService) DeleteById(id int) error {
// 校验数据是否存在(按需补充,可选)
count, err := s.Dao.WherePri(id).Count()
if err != nil {
g.Log().Error(err)
return myerrors.DbError("查询XXX数据失败")
}
if count == 0 {
return myerrors.TipsError("XXX数据不存在")
}
// 删除操作
_, err = s.Dao.WherePri(id).Delete()
if err != nil {
g.Log().Error(err)
return myerrors.DbError("删除XXX失败")
}
// TODO: 写业务动态日志(必须实现,不可删除)
return nil
}
// DeleteByIds 根据ID批量删除(固定方法名,与handler保持一致)
func (s *xxxService) DeleteByIds(ids []int64) error {
if len(ids) == 0 {
return myerrors.TipsError("请选择需要删除的XXX")
}
// 删除操作
_, err := s.Dao.WhereIn(s.Dao.C.Id, ids).Delete()
if err != nil {
g.Log().Error(err)
return myerrors.DbError("批量删除XXX失败")
}
// TODO: 写业务动态日志(必须实现,不可删除)
return nil
}
// GetById 根据ID查询单条(可选,按需补充)
func (s *xxxService) GetById(id int) (*xxxmodel.XxxRsp, error) {
entity, err := s.Dao.WherePri(id).Get()
if err != nil {
g.Log().Error(err)
return nil, myerrors.DbError("查询XXX数据失败")
}
if entity.IsEmpty() {
return nil, myerrors.TipsError("XXX数据不存在")
}
var rsp xxxmodel.XxxRsp
if err := entity.Struct(&rsp); err != nil {
g.Log().Error(err)
return nil, myerrors.SystemError("数据转换失败")
}
return &rsp, nil
}
app/handler/<module>/xxx.go)package <module> // 替换为实际模块名,如work、proj
import (
"context"
"dashoo.cn/common_definition/comm_def"
xxxmodel "dashoo.cn/opms/app/model/<module>" // 替换为实际model别名,如workmodel
services "dashoo.cn/opms/app/service/<module>" // 替换为实际service别名,如workService
"github.com/gogf/gf/frame/g"
)
// XxxHandler 接口处理类(替换Xxx为模块名,如WorkOrderHandler)
type XxxHandler struct{}
// GetList 分页查询接口(固定方法名,与service保持一致)
func (h *XxxHandler) GetList(ctx context.Context, req *xxxmodel.XxxSearchReq, rsp *comm_def.CommonMsg) error {
// 初始化service
s, err := services.NewXxxService(ctx) // 替换Xxx为模块名,如NewWorkService
if err != nil {
return err
}
// 调用service方法
total, list, err := s.GetList(req)
if err != nil {
return err
}
// 统一返回格式(必须按此格式,不可修改)
rsp.Data = g.Map{"list": list, "total": total}
return nil
}
// Create 新增接口(固定方法名,与service保持一致)
func (h *XxxHandler) Create(ctx context.Context, req *xxxmodel.XxxAddReq, rsp *comm_def.CommonMsg) error {
s, err := services.NewXxxService(ctx)
if err != nil {
return err
}
if err := s.Create(req); err != nil {
return err
}
rsp.Data = g.Map{"msg": "新增成功"}
return nil
}
// UpdateById 更新接口(固定方法名,与service保持一致)
func (h *XxxHandler) UpdateById(ctx context.Context, req *xxxmodel.XxxUpdateReq, rsp *comm_def.CommonMsg) error {
s, err := services.NewXxxService(ctx)
if err != nil {
return err
}
if err := s.UpdateById(req); err != nil {
return err
}
rsp.Data = g.Map{"msg": "更新成功"}
return nil
}
// DeleteById 单条删除接口(固定方法名,与service保持一致)
func (h *XxxHandler) DeleteById(ctx context.Context, req *xxxmodel.XxxDeleteByIdReq, rsp *comm_def.CommonMsg) error {
s, err := services.NewXxxService(ctx)
if err != nil {
return err
}
if err := s.DeleteById(req.Id); err != nil {
return err
}
rsp.Data = g.Map{"msg": "删除成功"}
return nil
}
// DeleteByIds 批量删除接口(固定方法名,与service保持一致)
func (h *XxxHandler) DeleteByIds(ctx context.Context, req *xxxmodel.XxxDeleteByIdsReq, rsp *comm_def.CommonMsg) error {
s, err := services.NewXxxService(ctx)
if err != nil {
return err
}
if err := s.DeleteByIds(req.Ids); err != nil {
return err
}
rsp.Data = g.Map{"msg": "批量删除成功"}
return nil
}
main.go)// 新增模块注册(添加在现有注册代码之后,按模块顺序排列)
import (
// 导入新增模块handler
xxxhandler "dashoo.cn/opms/app/handler/<module>" // 替换为实际handler路径,如workhandler
)
func main() {
s := rpcx.NewServer()
// 现有模块注册...
// 新增模块注册(替换Xxx为模块名,如WorkOrder,handler为新增的handler实例)
s.RegisterName("Xxx", new(xxxhandler.XxxHandler), "") // 如 s.RegisterName("WorkOrder", new(workhandler.WorkOrderHandler), "")
}
app/service/base/base_product.go)
if req.Field != "" { dao = dao.Where... }Count() 后分页 Page(req.GetPage())service.DataScope 按需求启用:仅在需求明确要求时添加list + total,错误必须用 myerrors 封装app/service/work/work_order.go)
service.SetUpdatedInfo(...) 补齐审计字段FieldsEx(service.UpdateFieldEx...) 防止误改创建字段myerrors.TipsError("数据不存在")g.Map,调用 SetUpdatedInfo 补齐审计字段FieldsEx(service.UpdateFieldEx...),排除不可改字段app/service/proj/business.go)
TX(tx),确保原子性return err 回滚,同时打印错误日志app/service/proj/business.go、app/service/work/work_order.go)
ProcessType/Result,再落状态bizCode(格式、关联业务是否存在)、ProcessType(是否匹配当前业务)、Result(是否为合法值)app/handler/contract/ctr_contract.go)
rsp.Data = g.Map{...},禁止直接返回其他格式handler 直连 DAO(包括查询、新增、更新、删除),不得在 model 写业务逻辑、定义方法。SetCreatedInfo/SetUpdatedInfo,更新时必须添加 FieldsEx(service.UpdateFieldEx...),排除不可改字段。"10"/"20"/"30"... 这类业务状态码不可擅自重定义,新增状态需在 model 中定义常量并同步到规范。app/dao/**/internal、app/model/**/internal 仅可通过 GF 工具生成流程更新,不手改结构、字段、注释。context.WithValue(..., "contextService", s) 与 DataScope(...);未要求时禁止默认添加。opms_libary/myerrors 封装,用户可见错误用中文提示,系统错误需打印日志。g.Map 用于 DAO 写入时,必须使用 gconv.Map(req),禁止使用 gconv.Struct(req, &g.Map{}),后者运行时会报 params should be type of pointer, but got type: map 错误。handler -> service -> dao -> model 分层,无跨层操作main.go 注册,文件齐全GetList、Create、UpdateById、DeleteById、DeleteByIds,与 handler 保持一致dao 与 model 导入是否使用别名(如 xxxDao、xxxModel),格式正确myerrors 封装,用户可见错误为中文提示,列表返回格式为 list+totalgconv.Map(req),而非 gconv.Struct(req, &g.Map{})micro_request.postRequest,禁止使用 request.js 发起 REST 调用main.go 中 s.RegisterName 注册的名称完全一致GetList、Create)process.env.VUE_APP_ParentPath,而非硬编码路径parseInt(projectId))不要假设。不要掩饰困惑。明确呈现权衡。
在实现之前:
只写解决问题所需的最少代码。不做任何预设性扩展。
问问自己:“一个资深工程师会认为这太复杂了吗?” 如果答案是会,那就继续简化。
只改必须改的内容。只清理你自己造成的问题。
编辑现有代码时:
当你的改动产生遗留项时:
检验标准:每一行改动都应当能直接追溯到用户请求。
先定义成功标准,再循环推进,直到验证通过。
把任务转换成可验证的目标:
对于多步骤任务,先给出简短计划:
1. [步骤] → 验证:[检查项]
2. [步骤] → 验证:[检查项]
3. [步骤] → 验证:[检查项]
强有力的成功标准能让你独立闭环推进。弱成功标准(“把它弄好”)则会不断需要额外澄清。