// Package port academic 域跨域只读接口(**academic 定义,org 侧实现/注入**)。 // // 依据:unified/03-migration/academic-domain-design.md §1.3(包结构与依赖方向)/ §1.6(跨库跨域收敛)。 // // 依赖方向铁律: // - academic → org(单向):本包只定义接口,实现放 port/orgadapter.go(只读转发)+ 装配层注入; // - org **不得**反向依赖 academic; // - 禁止跨库 SQL(`yuansiwei.xxx` 前缀)与跨域 JOIN——取数一律经本包接口 + 同事务查询。 // // 为什么需要 port: // - 旧 SP `custom_refund` / `cleared_balance` 跨库读 `yuansiwei.t_school_campus.self_support`, // `cancel_recharge_activity` 读 `school_id`;目标态校区数据归 org 域 `t_campus`(§11.1 拍板 A // 补 `self_support`),academic 只能通过接口读,不得直接 import org 的仓储做业务判断。 package port import ( "context" "gorm.io/gorm" ) // CampusInfo 校区只读视图(跨域取数的唯一载体,字段裁剪到 academic 所需最小集)。 // // 字段: // - CampusID int64:校区 ID(→ t_campus.campus_id)。 // - SchoolID int64:所属机构 ID(旧口径名 schoolid,即 t_campus.institution_id;作用域列取值源)。 // - SelfSupport bool:是否自营校区(t_campus.self_support:1 自营 / 0 加盟); // 决定 custom_refund / cleared_balance 的活动失效范围(`campus_id IN (x,0)` vs `= x`,设计 §11-2)。 // - Status string:校区状态(active/disabled;disabled 校区禁止发起资金操作)。 type CampusInfo struct { CampusID int64 SchoolID int64 SelfSupport bool Status string } // InstitutionID 机构 ID 的规范命名访问器(SchoolID 为旧口径字段名,语义等同 institution_id)。 // // 出参:int64——机构 ID(与 t_campus.institution_id 同值)。 func (c *CampusInfo) InstitutionID() int64 { if c == nil { return 0 } return c.SchoolID } // IsActive 校区是否可用(status=active)。 func (c *CampusInfo) IsActive() bool { return c != nil && c.Status == "active" } // CampusReader 校区只读读取器(academic 定义;实现见 OrgCampusReader,可 mock 单测)。 // // 契约: // - 校区不存在 → `*infraerrors.AppError{Code: 3104}`(apperr.ErrCampusNotFound),不返回 gorm 原错; // - 只读,不写 org 表; // - 同一事务内只读的强一致场景(如退费事务里判自营),用可选的 TxCampusReader 扩展接口。 type CampusReader interface { // GetByID 按校区 ID 取校区只读视图。 // // 入参:ctx 请求上下文;campusID 校区 ID(<=0 视为业务参数错 3140)。 // 出参:*CampusInfo(命中);error——apperr.ErrCampusNotFound(3104) / infraerrors.ErrDBError(5002)。 GetByID(ctx context.Context, campusID int64) (*CampusInfo, error) // GetSchoolID 按校区 ID 取所属机构 ID(旧 SP 的 `SchoolCampusHelper::getInfo($campusid)->school_id`)。 // // 入参:ctx 请求上下文;campusID 校区 ID。 // 出参:int64 机构 ID;error——apperr.ErrCampusNotFound(3104) / infraerrors.ErrDBError(5002)。 GetSchoolID(ctx context.Context, campusID int64) (int64, error) // IsSelfSupport 校区是否自营(决定活动失效范围的判据,设计 §1.6 / §11-2)。 // // 入参:ctx 请求上下文;campusID 校区 ID。 // 出参:bool 自营=true;error——apperr.ErrCampusNotFound(3104) / infraerrors.ErrDBError(5002)。 IsSelfSupport(ctx context.Context, campusID int64) (bool, error) } // TxCampusReader 同事务只读扩展接口(可选实现)。 // // 使用场景:资金事务内需要"与写操作同一快照"的校区判定(旧 SP 是同库 JOIN 语义), // 传 tx 保证读到的校区行与事务可见性一致;不传 tx 的 GetByID 走连接池默认隔离级别。 type TxCampusReader interface { // GetByIDTx 在给定事务句柄上读取校区(只读,不写)。 // // 入参:ctx 请求上下文;tx 事务句柄(可为 nil → 退化到连接池);campusID 校区 ID。 // 出参:*CampusInfo;error 语义同 CampusReader.GetByID。 GetByIDTx(ctx context.Context, tx *gorm.DB, campusID int64) (*CampusInfo, error) }