| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- // 课程域请求 / 响应 DTO(端点 44~57 的**入参形状**与响应拼装)——AC-13c。
- //
- // 依据:unified/03-migration/academic-frontend-contract-v1.md
- // - §2.4 课程/排班/打卡(端点 44~57 的 method/path/角色/请求要点);
- // - §3.4 C 表(响应字段名,**唯一权威**)+ 总原则 ③「字段准入条件」;
- // - §1.5 B1/B2(时间与量纲:`startTime`/`endTime` 是业务时间列,金额是两位小数字符串)。
- //
- // ---------------------------------------------------------------------------
- // 三条 DTO 铁律(与 `dto_fund.go` / `dto_activity.go` 同源,**不另立一套**)
- // ---------------------------------------------------------------------------
- //
- // 1. **金额一律 `string`**(`"100.00"`),Handler 用 `parseMoney` / `parseMoneyOrZero` 解析;
- // 不声明 float64(浮点误差)也不声明 money.Amount(会让 JSON number 被静默接受)。
- // 2. **缺参 → 2001 由 `validate` tag 表达;值非法 → 2002 由 Handler 显式判**。枚举字段
- // 不写 `oneof=`(`oneof` 失败会被归成 2001,而"传了非法枚举"属 2002)。
- // 3. **响应字段逐个列溯源**:每个 JSON 键都必须能追到一个具体列,写不出来的一律不做
- // (本批因此删了 `room` 与 `divide[].level`,并保留了 `week` 的字符串原值——见各结构注释)。
- //
- // ---------------------------------------------------------------------------
- // 本批**不接受**的请求字段(写了也无效,故不声明)
- // ---------------------------------------------------------------------------
- //
- // institutionId 由请求作用域决定(非超管传别的机构 → 1004,由 `resolveInstitutionID` 判)
- // status(课程) 发布状态机专用入口,不在 CRUD 里改(AC-11 `CreateCourse` 恒落"未发布")
- // numberSign /
- // unpaid / unpaid 类欠费列 —— **废弃列**(2018-05 停更),任何入参都不写它
- package handler
- import "time"
- // ---------------------------------------------------------------- 端点 45:课程 CRUD
- // CreateCourseReq 端点 45 新建课程请求体(`POST /admin/courses`)。
- //
- // 字段 → 目标列(DDL 第四部分 t_courses):
- //
- // name→name / subjectId→subject_id / gradeId→grade_id / type→type / classifyId→classify_id /
- // number→number / duration→duration / week→week / startTime→start_time / endTime→end_time /
- // address→address / originalCost→original_cost / realPrice→real_price /
- // discountCash→discount_cash / discountRatio→discount_ratio / maxPeople→max_people / remark→remark
- type CreateCourseReq struct {
- // Name 课程名称(必填)。
- Name string `json:"name" validate:"required,max=500"`
- // SubjectID 科目(0 = 混合)。
- SubjectID int32 `json:"subjectId"`
- // GradeID 年级(0 = 混合)。
- GradeID int32 `json:"gradeId"`
- // Type 班型编码(0 → 1000 集体班)。
- Type int32 `json:"type"`
- // ClassifyID 课程分类(0 = 不挂分类;>0 时须存在且同作用域,否则 3001)。
- ClassifyID int64 `json:"classifyId"`
- // Number 上课次数(>=0)。
- Number int32 `json:"number"`
- // Duration 单节课时长("1.5" 这类课时字符串,1 位小数;**必填**,>0)。
- Duration string `json:"duration" validate:"required,max=10"`
- // Week 周规则("-1" 全部 / "0" 周日 / "1".."6" / 逗号多值;空 → "-1")。
- Week string `json:"week" validate:"max=50"`
- // StartTime 课程有效期起(RFC3339 或 `2006-01-02`;可空)。
- StartTime string `json:"startTime"`
- // EndTime 课程有效期止(同上;不得早于 StartTime → 否则 2002)。
- EndTime string `json:"endTime"`
- // Address 上课地址。
- Address string `json:"address"`
- // OriginalCost 原价(元字符串;可空 → "0.00")。
- OriginalCost string `json:"originalCost"`
- // RealPrice 实价(元字符串;可空 → "0.00")。
- RealPrice string `json:"realPrice"`
- // DiscountCash 现金折扣(元字符串;**允许负**,模型红线)。
- DiscountCash string `json:"discountCash"`
- // DiscountRatio 折扣率(整数百分比,仅展示/试算)。
- DiscountRatio int32 `json:"discountRatio"`
- // MaxPeople 最多人数(0 → 由修复 SP 从分类继承)。
- MaxPeople int32 `json:"maxPeople"`
- // Remark 备注。
- Remark string `json:"remark"`
- }
- // UpdateCourseReq 端点 45 更新课程请求体(`PUT /admin/courses/:id`)。
- //
- // 语义:**部分更新**(nil = 不改)。与新建的唯一差异是"全部可空",因此一律用指针,
- // 以便区分"没传"(不改)与"传了零值"(改成 0 / 空串)。
- type UpdateCourseReq struct {
- // Name 课程名称(传了就不得为空)。
- Name *string `json:"name"`
- // SubjectID 科目。
- SubjectID *int32 `json:"subjectId"`
- // GradeID 年级。
- GradeID *int32 `json:"gradeId"`
- // Type 班型。
- Type *int32 `json:"type"`
- // ClassifyID 分类(>=0;>0 须存在)。
- ClassifyID *int64 `json:"classifyId"`
- // Number 上课次数(>=0)。
- Number *int32 `json:"number"`
- // Duration 单节课时长(课时字符串,>0)。
- Duration *string `json:"duration"`
- // Week 周规则。
- Week *string `json:"week"`
- // StartTime 有效期起。
- StartTime *string `json:"startTime"`
- // EndTime 有效期止。
- EndTime *string `json:"endTime"`
- // Address 上课地址。
- Address *string `json:"address"`
- // OriginalCost 原价。
- OriginalCost *string `json:"originalCost"`
- // RealPrice 实价。
- RealPrice *string `json:"realPrice"`
- // DiscountCash 现金折扣(允许负)。
- DiscountCash *string `json:"discountCash"`
- // DiscountRatio 折扣率。
- DiscountRatio *int32 `json:"discountRatio"`
- // MaxPeople 最多人数。
- MaxPeople *int32 `json:"maxPeople"`
- // Remark 备注。
- Remark *string `json:"remark"`
- }
- // ---------------------------------------------------------------- 端点 47:新建排班规则
- // CreateClassReq 端点 47 新建排班规则请求体(`POST /admin/courses/:id/classes`)。
- //
- // 时分用 `"HH:MM"` 字符串(而非 datetime):旧库该两列是"时分语义",date 部分无业务含义。
- type CreateClassReq struct {
- // ClassID 班级 ID(旧库外部班级 id;0 = 不指定)。
- ClassID int64 `json:"classId"`
- // TeacherID 老师 ULID(可空:空 = 未定老师;给了才会做 3122 冲突判定)。
- TeacherID string `json:"teacherId" validate:"max=26"`
- // Week 周规则("-1"/"0".."6"/逗号多值;空 → "-1")。
- Week string `json:"week" validate:"max=50"`
- // Start 上课时分(`"09:00"`;**必填**)。
- Start string `json:"start" validate:"required,max=5"`
- // End 下课时分(`"10:30"`;可空 → 按课程 duration 推算;给了必须晚于 Start)。
- End string `json:"end" validate:"max=5"`
- // StandardLessonCost 课程标准单价(元字符串;可空 → "0.00")。
- StandardLessonCost string `json:"standardLessonCost"`
- // LessonCost 一次课课费(元字符串;可空 → "0.00";打卡扣课时依据)。
- LessonCost string `json:"lessonCost"`
- // DiscountCash 课程优惠(元字符串;允许负)。
- DiscountCash string `json:"discountCash"`
- // Number 开课总量(课次;>=0)。
- Number int32 `json:"number"`
- // Address 上课地址。
- Address string `json:"address"`
- // Remark 备注。
- Remark string `json:"remark"`
- }
- // ---------------------------------------------------------------- 端点 49:生成课表
- // GenerateRecordsReq 端点 49 生成课表请求体(`POST /admin/courses/:id/records/generate`)。
- //
- // 两个字段都可空(空 → days=7、from=今天),但**传了必须合法**(非法 → 2002)。
- type GenerateRecordsReq struct {
- // Days 生成窗口天数(<=0 → 7;>366 → 钳制 366)。
- Days int `json:"days"`
- // From 窗口起点(`2006-01-02` 或 RFC3339;只取日期部分;空 → 服务端当天)。
- From string `json:"from"`
- }
- // ---------------------------------------------------------------- 端点 51 / 52:打卡明细
- // AttendanceItemReq 单条打卡明细(端点 52 请求 `list[]` 元素,端点 51 的 `items[]` 同构)。
- //
- // 字段 → 目标列(t_courses_record_student):
- //
- // userId→student_id / type→type / pay→pay / cost→cost / giveCost→give_cost / remark→remark
- //
- // ⚠ `pay` 是**是否已支付**(int8 0/1),不是金额;金额是 `cost`。契约 §3.4 C 行 52 明确
- // 请求体为 `list:[{userId,type,pay,cost,giveCost}]`(mock 旧名 `attendance/payCost` 已废弃)。
- type AttendanceItemReq struct {
- // UserID 学生 ULID(必填)。
- UserID string `json:"userId" validate:"required,max=26"`
- // Type 上课类型(1 正常 / 2 插班 / 3 赠送;0 → 1;其它 → 2002)。
- Type int32 `json:"type"`
- // Pay 是否已支付(0 未付 / 1 已付;nil = 不改,结课时按"已报课即已付")。
- Pay *int8 `json:"pay"`
- // Cost 本次课费(元字符串;可空 = 取报课记录的 lesson_cost 快照)。
- Cost string `json:"cost"`
- // GiveCost 本次使用赠送额(元字符串;可空 → 0)。
- GiveCost string `json:"giveCost"`
- // Remark 备注。
- Remark string `json:"remark"`
- }
- // MarkAttendanceReq 端点 52 打卡请求体(`POST /admin/courses/records/:recordId/attendance`)。
- type MarkAttendanceReq struct {
- // List 打卡明细(必填非空;同一学生出现两次 → 3140)。
- List []AttendanceItemReq `json:"list" validate:"required,min=1,dive"`
- }
- // FinishRecordReq 端点 51 结束上课请求体(`POST /admin/courses/records/:recordId/finish`)。
- type FinishRecordReq struct {
- // Items 本次打卡明细(**可空**:空 = 按在读学生全勤自动生成,成本取 lesson_cost 快照)。
- Items []AttendanceItemReq `json:"items"`
- // ActualEndTime 实际下课时间(RFC3339 或 `2006-01-02`;空 → 服务端当前时刻)。
- ActualEndTime string `json:"actualEndTime"`
- // RewardType 课酬结算方式(0 → 1 正常 / 2 按原课程老师课酬无最低课酬;其它 → 2002)。
- RewardType int8 `json:"rewardType"`
- // Remark 结课备注(当前仅日志展示,不落库——`t_courses_record.remark` 属排班备注)。
- Remark string `json:"remark"`
- }
- // ---------------------------------------------------------------- 端点 54:报名
- // EnrollReq 端点 54 报名请求体(`POST /admin/courses/:id/students`)。
- //
- // ⚠ **契约 §2.4 端点 54 只给了响应字段,没有入参形状** ⇒ 本请求体是 AC-13c 自定义的
- // 最小可对账集合(诊断见 `service.EnrollInput` 注释),已在交付报告登记为「契约缺口待补」。
- type EnrollReq struct {
- // UserID 学生 ULID(必填)。
- UserID string `json:"userId" validate:"required,max=26"`
- // ClassID 班级 ID(0 = 不指定)。
- ClassID int64 `json:"classId"`
- // CampusID 校区(0 = 用作用域校区;>0 且与作用域校区不一致 → 1004)。
- CampusID int64 `json:"campusId"`
- // Pay 本次实收(元字符串,必填;**不写资金流水**,只做报课行快照)。
- Pay string `json:"pay" validate:"required"`
- // RemainderReceivable 剩余应收(元字符串;可空 → 0 = 全额缴清)。
- RemainderReceivable string `json:"remainderReceivable"`
- // LessonCost 单节课费(元字符串,必填;扣课时依据)。
- LessonCost string `json:"lessonCost" validate:"required"`
- // Number 课次(>=0)。
- Number int32 `json:"number"`
- // GiveNumber 赠送课次(>=0)。
- GiveNumber int32 `json:"giveNumber"`
- // DiscountCash 现金折扣(元字符串;允许负)。
- DiscountCash string `json:"discountCash"`
- // GiveCost 已用赠送额(元字符串;可空 → 0)。
- GiveCost string `json:"giveCost"`
- // RecommenderUserID 推荐人 ULID(可空;分账上行起点)。
- RecommenderUserID string `json:"recommenderUserId" validate:"max=26"`
- // Remark 备注。
- Remark string `json:"remark"`
- }
- // ---------------------------------------------------------------- 端点 56:欠费核销
- // CounteractArrearsReq 端点 56 欠费核销请求体(`POST /admin/courses/arrears/counteract`)。
- //
- // 契约 §2.4 行 56 原文:`studentId,institutionId,campusId,money`(**四个都必填**)。
- // `institutionId`/`campusId` 是 SP27 的**显式数据范围参数**(不是作用域推断),
- // 因此非超管传了与本作用域不一致的值 → **1004**(不是 2002)。
- type CounteractArrearsReq struct {
- // StudentID 学生 ULID(必填)。
- StudentID string `json:"studentId" validate:"required,max=26"`
- // InstitutionID 机构 ID(必填 >0)。
- InstitutionID int64 `json:"institutionId" validate:"required,gt=0"`
- // CampusID 校区 ID(必填 >0)。
- CampusID int64 `json:"campusId" validate:"required,gt=0"`
- // Money 待核销金额(元字符串,必填;<=0 → 服务端 no-op,不是错误)。
- Money string `json:"money" validate:"required"`
- }
- // ---------------------------------------------------------------- 响应拼装(只放契约要求的键)
- // courseSavedResp 端点 45 响应(契约 §3.4 C 行 45:只要求 `id`)。
- type courseSavedResp struct {
- // ID 课程 ID。
- ID int64 `json:"id"`
- }
- // courseRowView 端点 44 的行(契约 §3.4 C 行 44)。
- //
- // 键名 `classify`(不是 `classifyId`)是**契约既定**:前端按 `classify` 读,改名会静默空白。
- type courseRowView struct {
- // ID 课程 ID。
- ID int64 `json:"id"`
- // InstitutionID 机构 ID。
- InstitutionID int64 `json:"institutionId"`
- // Name 课程名称。
- Name string `json:"name"`
- // Classify 课程分类 ID(源列 `classify_id`;契约键名 `classify`)。
- Classify int64 `json:"classify"`
- // Status 课程状态(0 未发布 / 1 发布未开始 / 2 进行中 / 3 已结束)。
- Status int32 `json:"status"`
- // TeacherIDs 排班规则上的老师(去重升序;空 → [])。
- TeacherIDs []string `json:"teacherIds"`
- // RecordCount 排班实例条数(全状态)。
- RecordCount int64 `json:"recordCount"`
- }
- // classCreatedResp 端点 47 响应(契约 §3.4 C 行 47:`classId`)。
- type classCreatedResp struct {
- // ClassID 新规则的班级 ID(`t_courses_class.class_id`)。
- ClassID int64 `json:"classId"`
- }
- // classRowView 端点 46 的行(契约 §3.4 C 行 46 + 本批两处偏差)。
- //
- // ⚠ 与契约的两处差异(已在交付报告登记):
- //
- // ① `week` 下发**原值字符串**而不是 1~7 的数字:该列取值 `"-1"`(全部周)/`"0"`..`"6"`/
- // 逗号多值,"1~7 数字"表达不了「全部周」与「多值」;
- // ② **无 `room`**:`t_courses_class` 没有 room 列(只有 address/remark),按 §3.4 ③ 不下发。
- type classRowView struct {
- // ClassID 班级 ID(源列 `class_id`)。
- ClassID int64 `json:"classId"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- // TeacherID 老师 ULID(可空 → 空串)。
- TeacherID string `json:"teacherId"`
- // Week 周规则原值("-1"/"0".."6"/逗号多值)。
- Week string `json:"week"`
- // Start 上课时分(**date 部分恒 1970-01-01**,只取时分;见 model.CoursesClass 注释)。
- Start *time.Time `json:"start"`
- // End 下课时分(可空)。
- End *time.Time `json:"end"`
- // CampusID 校区 ID。
- CampusID int64 `json:"campusId"`
- }
- // recordRowView 端点 48 的行(契约 §3.4 C 行 48)。
- //
- // ⚠ **无 `room`**(同端点 46:`t_courses_record` 只有 `address`,没有 room 列)。
- type recordRowView struct {
- // RecordID 排班实例 ID。
- RecordID int64 `json:"recordId"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- // TeacherID 老师 ULID。
- TeacherID string `json:"teacherId"`
- // CampusID 校区 ID。
- CampusID int64 `json:"campusId"`
- // StartTime 上课时间。
- StartTime *time.Time `json:"startTime"`
- // EndTime 下课时间。
- EndTime *time.Time `json:"endTime"`
- // Status 上课状态(0 未开始 / 1 上课中 / 2 已结束)。
- Status int32 `json:"status"`
- // StudentCount 上课人数(源列 `student_number`,结课时写入)。
- StudentCount int32 `json:"studentCount"`
- }
- // generateRecordsResp 端点 49 响应(契约 §3.4 C 行 49)。
- type generateRecordsResp struct {
- // Generated 本次新建的排班实例条数(幂等:第二次为 0)。
- Generated int `json:"generated"`
- // Days 生效的生成窗口天数(已钳制)。
- Days int `json:"days"`
- // Changed 本次是否真的写了库(false = 无新增)。
- Changed bool `json:"changed"`
- }
- // startRecordResp 端点 50 响应(契约 §3.4 C 行 50)。
- type startRecordResp struct {
- // RecordID 排班实例 ID。
- RecordID int64 `json:"recordId"`
- // Status 新状态(恒 1 = 上课中)。
- Status int32 `json:"status"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- }
- // attendanceResp 端点 52 响应(契约 §3.4 C 行 52:`recordId` + `checked`)。
- type attendanceResp struct {
- // RecordID 排班实例 ID。
- RecordID int64 `json:"recordId"`
- // Checked 当前打卡人数(= 该实例的打卡行总数,含此前已打卡的)。
- Checked int32 `json:"checked"`
- }
- // divideGrantView 端点 51 `divide[]` 的元素 —— **团队裁决后的最终形状 `[{userId, type, amount}]`**。
- //
- // 与契约 v1.6 mock 的差异(**已批准,待契约升版,前端清单由 team-lead 统一出**):
- //
- // 删 `level` —— `t_user_divide_into_log` 无层级列;`t_user_divide_into.layer` 是当前层级,
- // 不是入账当时的层级,用当前值顶替 = 伪造历史。
- // 删 `quota` —— 节点 quota 事后可被 `update_quota` 改,日志未留"当时额度",同理不得顶替。
- // 增 `type` —— 源列 `type`(1 一次付清 / 2 按课次),语义清晰且**确实存在于日志行上**。
- // 增 `userId`— 源列 `user_id`(谁分了这笔钱),是数组最关键的维度。
- // 留 `amount`— 源列 `money`。
- //
- // ⚠ 不得为了迁就旧 mock 给 `level` 填常量(有值、恒为某常数、永远查不出是假的)。
- type divideGrantView struct {
- // UserID 获得分成的用户(源列 `t_user_divide_into_log.user_id`)。
- UserID string `json:"userId"`
- // Type 分成类型(源列 `type`:1 一次付清 / 2 按课次)。
- Type int32 `json:"type"`
- // Amount 本次入账金额(源列 `money`)。
- Amount string `json:"amount"`
- }
- // payrollTraceView 端点 51 `payroll` 对象。
- type payrollTraceView struct {
- // DetailID 命中的课酬规则 ID(0 = 未配置方案/无法定位)。
- DetailID int64 `json:"detailId"`
- // Reward 本课课酬合计(源列 `t_courses_record.teacher_reward`,**落库值**)。
- Reward string `json:"reward"`
- }
- // perStudentView 端点 51 `perStudent[]` 的元素(契约 §3.4 C 行 51,字段名与端点 52 请求同义)。
- type perStudentView struct {
- // UserID 学生 ULID。
- UserID string `json:"userId"`
- // Type 上课类型(1 正常 / 2 插班 / 3 赠送)。
- Type int32 `json:"type"`
- // Pay 是否已支付。
- Pay bool `json:"pay"`
- // Cost 本次课费。
- Cost string `json:"cost"`
- // GiveCost 本次使用赠送额。
- GiveCost string `json:"giveCost"`
- // Counteracted 该学生本次是否触发了分账入账(源列:本次是否新增
- // `t_user_divide_into_log` 行;**不是**欠费核销,契约命名与语义在报告登记)。
- Counteracted bool `json:"counteracted"`
- }
- // finishRecordResp 端点 51 响应(契约 §3.4 C 行 51)。
- type finishRecordResp struct {
- // RecordID 排班实例 ID。
- RecordID int64 `json:"recordId"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- // Status 上课状态(恒 2 = 已结束)。
- Status int32 `json:"status"`
- // Changed 本次是否真的写了库(false = 幂等命中:未重复扣课时/未重复分账)。
- Changed bool `json:"changed"`
- // TotalSpend 本次扣课时合计(= `t_courses_record.income` = Σ record_student.cost)。
- TotalSpend string `json:"totalSpend"`
- // Divide 本次分账入账明细(空 → [])。
- Divide []divideGrantView `json:"divide"`
- // Payroll 课酬溯源。
- Payroll payrollTraceView `json:"payroll"`
- // PerStudent 逐学生打卡明细(空 → [])。
- PerStudent []perStudentView `json:"perStudent"`
- }
- // studentRowView 端点 53 的行(契约 §3.4 C 行 53)。
- //
- // `realName` 来源:`t_user.display_name`,经 `port.UserNameReader` 跨域只读(D4 裁决)。
- // ⚠ **查不到时下发 `null` 而不是空串**:空串会让前端无法区分"没有这个人"与"姓名为空";
- // 端口未注入(nil)时同样是 `null`(页面少一列,不影响其它字段)。
- type studentRowView struct {
- // UserID 学生 ULID(源列 `student_id`)。
- UserID string `json:"userId"`
- // RealName 学生显示名(源列 `t_user.display_name`;查不到 → null)。
- RealName *string `json:"realName"`
- // Pay 应收(源列 `receivable`)。
- Pay string `json:"pay"`
- // Cost 实收(源列 `net_receipts`)。
- Cost string `json:"cost"`
- // GiveCost 已用赠送额(源列 `give_cost`)。
- GiveCost string `json:"giveCost"`
- // RemainderReceivable 剩余应收(源列 `remainder_receivable`,**欠费真值**)。
- RemainderReceivable string `json:"remainderReceivable"`
- // Status 报课状态(1 正常 / 2 结束,数字码)。
- Status int32 `json:"status"`
- }
- // enrollResp 端点 54 响应(契约 §3.4 C 行 54)。
- type enrollResp struct {
- // Enrolled 是否已报名(成功恒 true)。
- Enrolled bool `json:"enrolled"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- // UserID 学生 ULID。
- UserID string `json:"userId"`
- }
- // arrearsRowView 端点 55 的行(契约 §3.4 C 行 55)。
- type arrearsRowView struct {
- // StudentID 学生 ULID。
- StudentID string `json:"studentId"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- // RemainderReceivable 剩余应收(>0;**唯一欠费口径**)。
- RemainderReceivable string `json:"remainderReceivable"`
- // Counteracted 该报课行是否发生过实收/核销(源列 `net_receipts > 0`;契约未定义判据,见报告)。
- Counteracted bool `json:"counteracted"`
- }
- // counteractResp 端点 56 响应(契约 §3.4 C 行 56)。
- type counteractResp struct {
- // Counteracted 本次是否真的核销了金额(false = 无欠费/金额<=0,服务端 no-op)。
- Counteracted bool `json:"counteracted"`
- // StudentID 学生 ULID。
- StudentID string `json:"studentId"`
- // Amount 实际核销总额(元;未核销 → "0.00")。
- Amount string `json:"amount"`
- }
- // scheduleRowView 端点 57 的行(**字段名待移动端契约定稿**,见 service.ScheduleRow 注释)。
- type scheduleRowView struct {
- // RecordID 排班实例 ID。
- RecordID int64 `json:"recordId"`
- // CourseID 课程 ID。
- CourseID int64 `json:"courseId"`
- // CourseName 课程名称(回查 t_courses.name)。
- CourseName string `json:"courseName"`
- // TeacherID 老师 ULID。
- TeacherID string `json:"teacherId"`
- // CampusID 校区 ID。
- CampusID int64 `json:"campusId"`
- // StartTime 上课时间。
- StartTime *time.Time `json:"startTime"`
- // EndTime 下课时间。
- EndTime *time.Time `json:"endTime"`
- // Status 上课状态(0/1/2)。
- Status int32 `json:"status"`
- // Cost 本次课费(无打卡行 → "0.00")。
- Cost string `json:"cost"`
- // GiveCost 本次使用赠送额(无打卡行 → "0.00")。
- GiveCost string `json:"giveCost"`
- // Pay 是否已支付(无打卡行 → false)。
- Pay bool `json:"pay"`
- // Checked 是否已有本人的打卡行。
- Checked bool `json:"checked"`
- }
|