| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- // 收支端点(端点 60)请求 DTO / 响应视图——AC-13d 第三批。
- //
- // 依据:unified/03-migration/academic-frontend-contract-v1.md §3.4 D 表 60(v1.9 重写)。
- //
- // ---------------------------------------------------------------------------
- // 契约原写法与目标态的四处冲突,逐条落地(v1.9 已裁决,这里只写明"为什么")
- // ---------------------------------------------------------------------------
- //
- // ① 原行字段 `direction` **删除**:与 `type` 是同一事实的两个名字,两张表都没有该列。
- // 前端用 `type` 判方向(1 支出 / 2 收入)。留着一个"看起来能读"的字段,迟早有人拿它
- // 和 `type` 比对,然后发现两者永远一致(因为它俩本来就是一件事)。
- // ② 原 `name` 在**记录表不存在** ⇒ 由字典 `t_expenditure.name` 提供(后端补齐,前端不拼)。
- // ③ 原行**缺 `expenditureId`** ⇒ 补上:只有 `name` 没有 id,前端"编辑"按钮无法回填
- // (名称可重名,且改一次名就对不上了)。
- // ④ 原 `userId` ⇒ **`createUserId`**:记录表只有 `create_user_id`,语义是**记账人**。
- // 契约里 `userId` 读起来像"收付对象",而"收付对象"这一列**不存在**(要加列 + 迁移)。
- // 名字不改,前端会把"谁记的账"当"付给谁"展示 —— 数据有值、语义全错。
- //
- // 另:`money` ← `cost`(列名 `cost`,契约字段名 `money`;不改前端字段名,只映射)。
- package handler
- import "time"
- // CreateExpenditureLogReq 端点 60 记账请求体(`POST /admin/expenditures`)。
- //
- // 字段 → 目标列(`t_expenditure_log`):
- //
- // institutionId → institution_id(**必填 >0**;不从 token 猜,超管要能替机构记账)
- // campusId → campus_id(0 = 机构级;未传且有校区作用域时取作用域校区)
- // expenditureId → expenditure_id(必填 >0;须在字典中且对本机构可见,否则 3001)
- // money → cost(**元字符串**,必填 > 0;方向由字典 `type` 决定,本列恒存正数)
- // remark → remark(可空)
- //
- // ⚠ 三个"故意不收"的字段:
- //
- // `bizNo` 后端生成(26 位 ULID)。让前端传单号 = 可枚举、可撞唯一键。
- // `type` 来自字典 `t_expenditure.type`,**不是入参**:否则"支出类型 + 收入方向"这种
- // 自相矛盾的记录会被写进去,报表净额怎么算都是错的。
- // `createUserId` 取当前登录用户(作用域 `userId`),不接受前端指定(否则可伪造记账人)。
- type CreateExpenditureLogReq struct {
- // InstitutionID 机构 ID(必填 >0;非超管须与作用域机构一致,否则 1004)。
- InstitutionID int64 `json:"institutionId"`
- // CampusID 校区 ID(0 = 机构级;负数 → 2002)。
- CampusID int64 `json:"campusId"`
- // ExpenditureID 收支类型 ID(必填 >0;字典 `t_expenditure.id`)。
- ExpenditureID int64 `json:"expenditureId" validate:"required,gt=0"`
- // Money 金额(元字符串,如 "100.00";必填且 > 0)。
- Money string `json:"money" validate:"required"`
- // Remark 备注(可空;超长 → 2002)。
- Remark string `json:"remark" validate:"max=2000"`
- }
- // expenditureLogView 收支记录行(契约 D 表 60 的 9 个字段)。
- //
- // 字段 → 来源列(**逐列溯源**,契约 §3.4 ③ 门槛):
- //
- // id ← t_expenditure_log.id
- // bizNo ← biz_no
- // expenditureId ← expenditure_id
- // type ← t_expenditure.type(**字典**;记录表无此列)
- // name ← t_expenditure.name(**字典**;记录表无此列)
- // money ← cost(金额,恒 2 位小数字符串)
- // createUserId ← create_user_id(可空 → `null`;**记账人**,不是收付对象)
- // campusId ← campus_id
- // createTime ← create_time
- //
- // 契约里**不再有** `direction` / `userId`(理由见文件头 ①④)。
- //
- // 字典未命中时的取值口径:`type=0`、`name=""`(**不编"未知类型"**——造的值追不到列,
- // 且前端按 `type` 分组时会被这个假值带偏)。
- type expenditureLogView struct {
- // ID 记录 ID。
- ID int64 `json:"id"`
- // BizNo 业务单号(对外唯一标识;不可变)。
- BizNo string `json:"bizNo"`
- // ExpenditureID 收支类型 ID(编辑表单回填用)。
- ExpenditureID int64 `json:"expenditureId"`
- // Type 收支方向(1 支出 / 2 收入;来自字典)。
- Type int32 `json:"type"`
- // Name 收支类型名称(来自字典)。
- Name string `json:"name"`
- // Money 金额(元,恒 2 位小数字符串)。
- Money string `json:"money"`
- // CreateUserID 记账人 ULID(可空 → `null`)。
- CreateUserID *string `json:"createUserId"`
- // CampusID 校区 ID(0 = 机构级)。
- CampusID int64 `json:"campusId"`
- // CreateTime 记账时间(`create_time`)。
- CreateTime time.Time `json:"createTime"`
- }
- // expenditureTypeView 收支字典项(契约:`types:[{id, type, name, parentId}]`)。
- //
- // 字段 → 来源列:`id` ← id;`type` ← type;`name` ← name;`parentId` ← parent_id(0 = 顶级)。
- // **不含 `institutionId`**:契约没给,且前端也用不上(平台通用字典与机构字典在同一个下拉里)。
- type expenditureTypeView struct {
- // ID 类型 ID。
- ID int64 `json:"id"`
- // Type 收支方向(1 支出 / 2 收入)。
- Type int32 `json:"type"`
- // Name 类型名称。
- Name string `json:"name"`
- // ParentID 父类型 ID(0 = 顶级;自引用树)。
- ParentID int64 `json:"parentId"`
- }
- // expenditureTypesResp 字典响应包裹(契约形状是 `{"types":[...]}`,不是裸数组)。
- type expenditureTypesResp struct {
- // Types 字典项(含平台通用字典,按 type/parentId/id 递增)。
- Types []expenditureTypeView `json:"types"`
- }
- // createdExpenditureLogView 记账响应(契约:POST → `{id, bizNo}`)。
- //
- // 只回这两个字段是**有意**的:金额/类型/时间都已经由请求确定,回显没有信息量;
- // 而 `bizNo` 是前端后续对账/查单的唯一 handle。
- type createdExpenditureLogView struct {
- // ID 新记录 ID。
- ID int64 `json:"id"`
- // BizNo 新记录业务单号。
- BizNo string `json:"bizNo"`
- }
|