数据模型
数据表由 JPA 按实体自动创建(ddl-auto: update),主键均为自增 id。所有实体带审计字段 create_time / update_time(JPA Auditing 自动维护;card_adjusts 仅 create_time);实体之间不使用外键约束,通过业务 id 字段在服务层关联。
范围说明:下列详表覆盖核心业务表;
acupoints、ad_schedules、ad_screens、dispense_logs、formula_items、formulas、herb_stock_logs、herbs、prescription_items、prescription_template_items、prescription_templates、recharges、review_customers、review_staffs、therapy_templates等表以实体与 api 为准(TokenEntity为死代码,不计入)。
customers — 顾客
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| name | String | 姓名(非空) |
| age | Integer | 年龄 |
| level | Integer | 会员等级 |
| phone | String | 手机号(唯一,大陆号段) |
| openid | String | 小程序 openid(唯一,可空);手机查卡 openid 路径按它绑定查卡,裸手机号查卡不做(2026-10-14 裁定) |
| gender | Integer | 性别(0 女、1 男) |
| address | String | 地址 |
| birthday | Date | 生日 |
| beauty_consent | Integer | 美容敏感信息单独同意:1 已同意 / 0 未同意(缺省 0;美容业态 d,PIPL 口径) |
| beauty_consent_time | Date | 同意时刻(同意接口写入) |
| beauty_consent_staff_id | Long | 经办员工 id(同意接口写入) |
索引:姓名、手机号、等级、生日、openid。@DynamicInsert:beauty_consent 缺省落 0。
customer_histories — 顾客病史(过敏史/既往史/护理方案)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| customer_id | Long | 顾客 id(非空,业务外键,索引) |
| type | Integer | 0 过敏史 / 1 既往史 / 2 护理方案(美容业态 d 档案扩展,非空) |
| content | String | 内容(非空,自由文本,≤200 字) |
逐条 append 记录(新增/删除,不做编辑),查询按 id 倒序。演示种子给两位演示顾客各补过敏/既往记录,见 api 顾客组;过敏审方只读 type=0,type=2 为美容护理方案文本。
customer_tags — 顾客标签(美容业态 d)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| customer_id | Long | 顾客 id(非空,索引) |
| tag | String | 标签(非空,≤20 字,如「肤质·油性」「偏好·力度大」) |
| create_time / update_time | Date | 审计时间 |
唯一约束 (customer_id, tag):同顾客同标签唯一。肤质/偏好轻量标签——过敏史不落标签(复用 customer_histories type 0)、护理方案走病史 type 2,不建并行档案;PIPL 单独同意记在 customers.beauty_consent。见 api 顾客组。
treats — 诊疗单(核心业务表)
中医诊疗记录,一个顾客可有多条:
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| cid | Long | 顾客 id(非空,业务外键) |
| staff_id | Long | 接诊医师 id(可空,staffs.id;开单署名语义=本次接诊由哪位医师执行,历史单可不署名,读取时按 staffs 回显姓名) |
| cdesc | TEXT | 主诉 |
| inquiry | TEXT | 问诊 |
| observation | TEXT | 望诊 |
| palpation | TEXT | 触诊 |
| pulse | String | 脉象 |
| pulse_left / pulse_right | TEXT | 左 / 右手脉象 |
| pulse_info | TEXT | 脉象解读 |
| five_ben_zi_* / five_zi_ben_* | String | 五行·子母关系(左/右手) |
| five_ben_ke_* / five_ke_ben_* | String | 五行·相克关系(左/右手) |
| five_nan_jin_*(大/小,左/右) | String | 难经取象 |
| five_rate_left / five_rate_right | TEXT | 五行比率 |
| five_aux_left / five_aux_right | TEXT | 五行辅助 |
| acupoint_left / acupoint_right | TEXT | 取穴(左/右手) |
| acu_method / manipulation | String | 针灸处方:针法(毫针/电针/温针…)、手法(补法/泻法/平补平泻…),≤30 字,空白按空处理 |
| moxa_method | String | 针灸处方:灸法(温和灸/雀啄灸/隔物灸…),≤60 字,空白按空处理 |
| retention_minutes | Integer | 针灸处方:留针分钟(1-240,可空) |
| acu_course | TEXT | 针灸处方:疗程/频次(自由文本,≤100 字) |
| diagnose | TEXT | 诊断 |
| plan | TEXT | 调理方案 |
| diet | TEXT | 饮食建议 |
| conditioning | TEXT | 调理过程 |
| review | TEXT | 回访记录 |
索引:顾客 id、创建时间。
诊疗时间线口径(2026-10-12 拍板,顾客档案「诊疗记录」列表):原型每条=项目名+疗程标签+摘要(医师·时长·证治取穴)。医师取 staff_id 署名(读取回显 staffName,未署名不标);时长取 retention_minutes 留针分钟(仅针灸接诊有值,无值不标——不伪造就诊时长);疗程标签取 acu_course;项目名不出——接诊单与项目/卡项/订单三处均无关联字段(treats 无 item_id、orders 无 treat_id、card_usages 无 treat_id),无关联即不伪造;项目名维度由同页「持卡卡项」面板与卡项疗程标记(见疗程口径)覆盖。若要做「接诊↔结算单」归属,需新增 orders.treat_id 并定归属语义,属独立需求。
acupoints(穴位字典):name(穴名唯一 1–10 字)、pinyin(全拼小写检索码,非空)、meridian(归经,非空)、location(体表定位)、indication(主治)、审计时间。经络穴位参考数据——接诊单取穴字段仍为自由文本,字典只做选穴辅助(按穴名/拼音检索、选中即追加),不参与其他业务校验。
items — 卡项
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| name | String | 名称(非空、唯一) |
| price | Integer | 价格 |
| description | TEXT | 描述 |
| enabled | Integer | 1 上架、0 下架(Kiosk 只展示上架项;存量行默认 1) |
| cover | String | 封面图相对 url(可空,/media 托管) |
| duration_minutes | Integer | 时长(分钟,可空,展示用) |
| category | Integer | 类目:0 医馆服务(缺省,存量行加列即为 0)/ 1 皮肤管理 / 2 身体护理 / 3 中医美容(美容业态 a,docs/design/salon-extension.md) |
| sort | Integer | 展示顺序,小者在前 |
reviews / review_staffs / review_customers — 反馈与复盘
| 表 | 字段要点 | 说明 |
|---|---|---|
reviews(每日总结) | day(唯一,归一为当日 0 点)、good、improvement | 全店每日总结,同日保存即覆盖 |
review_staffs(员工日复盘) | staff_id、name、cost(耗费)、done、advice、day | 按员工按日记录 |
review_customers(顾客回访) | customer_id、name、last(上次时间)、day(回访日)、advice | day 到期后出现在每日报表提醒 |
orders / recharges
| 表 | 字段要点 | 现状 |
|---|---|---|
orders(订单) | user_id(顾客)、item_id(卡项)、staff_id、status、price | 已接线:Kiosk 下单与前台建单均落 status=0 并快照卡项现价(前台建单记录 staff_id);status 语义 0 已下单 → 1 已确认 → 2 已完成,9 已取消(0/1→9,仅 9 可删),管理端分页与流转见 api 订单组;结算信息在 settlements 表(一单一结算),订单分页联出支付方式 |
recharges(储值流水) | user_id、money、note(≤100 字可空,退款补回写「退单 #结算id」) | 已接线:充值为正、储值支付扣减为负,余额 = 流水合计;储值退款原路退回落正数流水(日对账不计入充值);接口见 api 储值组 |
order_item(订单明细) | order_id、item_id、name、price、qty、subtotal | 多品项订单逐行落明细(name/price 为下单时刻快照,qty≥1,subtotal=单价×数量);订单接口统一联出 items[],存量单无明细行时按 itemId+price 回退一行数量 1 |
settlements — 结算单(收费结算)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键(结算单号,时间倒序展示) |
| order_id | Long | 订单 id(非空;原唯一索引已随退费口径移除——退款后重收一单可多张结算单,防重复收款改由「最近一张未退款」校验兜底) |
| user_id | Long | 顾客 id(非空,冗余自订单便于按顾客对账) |
| pay_type | Integer | 支付方式:1 储值 / 2 微信 / 3 支付宝 / 4 现金 / 5 次卡抵扣 |
| money | Integer | 实收金额(元,取订单价格快照;次卡抵扣记 0) |
| create_time / update_time | Date | 审计时间 |
索引:user_id(order_id 唯一索引已随退费口径移除,启动迁移 DROP 老库遗留索引)。结算动作把订单 0/1 → 2 已完成;储值支付同时在 recharges 落一条负数流水;次卡抵扣扣 customer_cards 余 1 次。微信/支付宝为演示口径(仅记录方式,无真实收银通道),见 api 收费组。
customer_cards — 顾客持卡(次卡)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| customer_id | Long | 顾客 id(非空,索引,customers.id) |
| item_id | Long | 卡项 id(非空,items.id) |
| total_times / remaining_times | Integer | 总次数 / 剩余次数(发卡=全量,抵扣减余次) |
| status | Integer | 1 有效 / 0 停用(列默认 1;停用卡不参与抵扣) |
| source_order_id | Long | 发卡来源订单(可空,前台手工发卡为空) |
| valid_until | Date | 有效期至(可空=长期有效),过期卡不参与抵扣 |
| create_time / update_time | Date | 审计时间 |
索引:顾客。抵扣按「同顾客 + 同卡项、有效未过期且有余次」取最早一张扣 1 次。接口见 api 次卡组。
疗程口径(2026-10-12 拍板):疗程=卡项——同项目第 N 张卡(按 create_time 购入先后)即第 N 疗程,由购卡流水天然推算,不建「顾客×卡项已完成次数」账本表;无卡项不标疗程(项目级就诊史不存在,不伪造首诊/复诊)。排班格位/接诊单顾客条按此口径展示,批量取卡走 GET /api/v1/card/customers。
refund_logs — 退款流水(退费)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| settlement_id | Long | 被退的结算单(非空,唯一索引——一笔结算只能退一次) |
| order_id | Long | 订单 id(非空,索引,回退订单状态用) |
| money | Integer | 退款金额(元,= 原结算实收;次卡为 0) |
| pay_type | Integer | 原支付方式冗余:1 储值 / 2 微信 / 3 支付宝 / 4 现金 / 5 次卡抵扣 |
| staff_id | Long | 操作人 staff id(可空) |
| reason | String | 退款理由(≤100 字,非空——退款必须能答「为什么」) |
| create_time / update_time | Date | 审计时间 |
退款 = 退整单、原路退回,无软删(演示口径无留痕);储值退回的正数流水随 recharges 天然留痕(note「退单 #结算id」,日对账不计入充值);次卡退款作废该订单 card_usages 并回补 customer_cards 余次;退款后订单 2 → 1 可重收。口径见 reconciliation.md。
card_usages — 次卡核销流水
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| card_id | Long | 持卡 id(非空,索引,customer_cards.id) |
| order_id | Long | 触发抵扣的订单(可空——老卡补录/历史迁移无单据) |
| staff_id | Long | 服务/操作员工(可空,取订单 staffId) |
| times_used | Integer | 本次是第几次消费(1 起 = 总次数 − 扣后余次) |
| create_time / update_time | Date | 审计时间 |
append-only:每次结算抵扣(payType=5)在同一事务落一条,随结算回滚;不做手工划扣。种子数据为演示卡片补了历史核销(无订单号)。接口见 api 次卡组。
card_adjusts — 持卡调整流水(疗程卡营销:赠次/转卡)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| card_id | Long | 持卡 id(非空,索引,customer_cards.id) |
| type | Integer | 1 赠次 / 2 转卡 |
| times | Integer | 赠加次数(1-99);转卡固定 0 |
| customer_id | Long | 转卡目标顾客 id(type=2 必填;赠次为空) |
| reason | String | 调整理由(非空,≤100 字,前端留痕展示) |
| staff_id | Long | 操作员工(可空,服务端取当前登录账号对应员工) |
| create_time | Date | 审计时间 |
append-only:赠次加 total/remaining 各 N(同一事务),转卡仅换 customer_id(余次/有效期照旧);停用卡不可操作(过期不拦——可先调期再用)。接口见 api 次卡组。
appointments — 预约(前台/馆长建约、小程序自助约期 → 到店接待 → 转接诊)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| customer_id | Long | 顾客 id(非空,customers.id) |
| item_id | Long | 预约卡项 id(可空=到店再定) |
| staff_id | Long | 接待员工 id(可空=到店分配) |
| room_id | Long | 工位 id(可空=不占工位维;美容项目预约占工位,rooms.id) |
| start_time | Date | 预约时段开始时刻(非空) |
| duration | Integer | 时长(分钟;自助约期固定 60) |
| status | Integer | 0 待到店 → 1 已接待(转接诊),0→9 取消;仅 9 可删 |
| remark | String | 备注(症状/需求);自助约期记「小程序自助」软标记,无 source 列 |
| create_time / update_time | Date | 审计时间 |
索引:顾客、卡项、工位、时段。接口见 api 预约组;同时段冲突校验:同医师同日同小时已有 status 0/1 的预约时拒绝(每时段限约 1 位,staffId 空组独立互斥);roomId 非空时同工位同小时同样互斥(技师+工位双维,美容业态 b,AppointmentRoomConflictHttpTest 锁死)。
commission_rules / commission_records — 提成(美容业态 c)
| 表 | 字段 | 说明 |
|---|---|---|
commission_rules(规则) | item_id(非空)、title(岗位,staffs.title 精确匹配)、ratio(整数百分比 0–100) | P0 固定比例「项目×岗位」,一岗一比例(唯一约束 item_id+title);医师/技师同表区分,不建并行实体;P1 再上规则引擎 |
commission_records(记录) | order_id、settlement_id、order_item_id(均非空)、staff_id(非空)、item_name/subtotal/ratio(快照)、amount(元,退单冲销为负)、remark | 结算完成自动生成(SettlementServiceImpl 落钩):业绩归属 = orders.staff_id,按 order_items 行小计 × 比例记(次卡抵扣实收 0 也记,整数元向下取整);insert-only,退单写等额负向冲销(remark「退单冲销 #结算id」)净额归零;存量单无明细行不记账 |
索引:规则 item_id + 唯一 (item_id,title);记录 order_id、settlement_id、staff_id。接口见 api 提成组。
rooms — 工位/房间(美容业态 b)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| name | String | 工位名(非空、唯一) |
| enabled | Integer | 1 在用、0 停用(存量行默认 1;新建预约表单只列在用工位) |
| create_time / update_time | Date | 审计时间 |
索引:名称。预约的资源维度——美容项目预约占工位、医馆预约不占(appointments.room_id 可空);与叫号展示文本 queue_calls.room 无关(那是展示字段,不是可预约资源)。见 api 工位组。
prescriptions / prescription_items — 中药处方
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键(处方号) |
| treat_id | Long | 接诊单 id(可空,treats.id) |
| customer_id | Long | 顾客 id(非空,customers.id) |
| staff_id | Long | 开方医师 id(可空,staffs.id) |
| doses | Integer | 剂数(几付,非空,默认 7) |
| decoction_status | Integer | 代煎状态:0 无需代煎 / 1 待煎 / 2 可取 / 3 已取(勾代煎开方落 1,流转 待煎→可取→已取 单向推进) |
| decoction_bags | Integer | 代煎袋数(=剂数;无需代煎为空) |
| prescription_type | Integer | 处方类型:0 汤剂(默认)/ 1 膏方 |
| paste_status | Integer | 膏方领取状态:0 非膏方 / 1 待制作 / 2 可取 / 3 已取(开膏方落 1,流转 待制作→可取→已取 单向推进) |
| craft | String | 收膏方式(仅膏方:炼蜜/清膏/糖膏/阿胶收膏等,可空;汤剂恒空) |
| dispense_status | Integer | 发药状态:0 待发药(默认)/ 1 已发药(开方落 0,单向不回退;代煎/膏方推进「已取」自动置 1) |
| dispense_time | Date | 发药时间(待发药为空) |
| dispense_by | Long | 发药操作人 staff_id(自动带出路径留空) |
| review_staff_id | Long | 药师审核人 staff_id(可空) |
| check_staff_id | Long | 调配 / 复核人 staff_id(可空) |
| delivery_type | Integer | 领取方式:0 自取(默认)/ 1 同城配送(代煎勾选取,可空) |
| usage / take_method | String | 煎法(水煎服,日一剂等)/ 服法(分早晚温服等,可空) |
| remark | String | 备注 |
| create_time / update_time | Date | 审计时间 |
prescription_items(药味):prescription_id(非空,索引)、herb(药名,非空,自由文本)、weight(单剂克数,Double,非空)、special(特殊煎法,可空)、process(炮制,可空)、sort(顺序,非空)。
herbs(药材字典):name(唯一,与处方药名精确同名比价)、price(每克分价,int,>0)、stock_min/stock_max(库存上下限,克,可空=不预警——余额接口低于下限或高于上限时标 warnStock,库房上下限预警口径)、dose_max(单剂克数上限,克/剂,可空=不预警——开方剂量审方 POST /prescription/dose-check 按此比对单剂克数,weight 严格大于上限才命中;由诊所自录,同库存上下限口径;药典权威剂量数据源仓内无,不硬编码)、审计时间。处方计价按字典实时试算(不落库、无快照,未收录药名不计费)。
dispense_logs(发药流水,单向追加):prescription_id(非空,索引)+ action(1 发药,本链路只有一种动作)+ staff_id(操作人,自动带出为空)+ note(≤100 字,自动带出写来源如「代煎领取自动发药」)+ 审计时间。重复发药 400、不可撤销;发药不扣库存、不落金额快照,见 药房与发药流转。
herb_stock_logs(饮片出入库流水):herb_id(非空,索引)+ type(1 入库 / 0 出库)+ quantity(克,正整数)+ expiry(批次效期,入库可带,出库恒空)+ supplier(≤50 字)+ note(≤100 字)+ 审计时间。库存 = 入库合计 - 出库合计(不落余额列,按流水重算);出库按效期先进先出(FEFO)消耗批次,余额接口据此给「最早未消耗批次效期 + 到期预警(默认 30 天)」;台账口径,不涉采购单据与结算,见 api 饮片库存组。
prescription_templates / prescription_template_items(病症模板,结构同下):模板侧多 doses/decoction/usage/remark 建议值与 enabled 上架位。formulas / formula_items(方剂库):formulas 为 name(方名唯一 1–20 字)、pinyin(全拼小写检索码,非空)、source(出处)、indication(功效主治);formula_items 为 formula_id(非空,索引)+ 药味四件套 herb/weight/special/sort。两者均为开方参考数据——模板按病症套用、方剂按方名/拼音检索带出全方,带出后随处方自由增减、不写回;方剂为文献常用量口径,不参与计价与审方。
therapy_templates(外治处方模板):name(模板名唯一,1–20 字,索引)+ method(外治方法,非空,≤10 字,索引)+ target(建议取穴/部位,≤100 字,可空)+ course(建议疗程/频次,≤100 字,可空)+ note(注意事项,≤100 字,可空)+ enabled(0 停用 / 1 启用)+ 审计时间。单表无明细(外治组合是「方法 + 部位 + 疗程」的自由文本,不像药方有逐味剂量)。接诊页「套用外治模板」只取 method/target/course 三个建议值回填针灸处方区与取穴文本,回填后随诊疗单自由改,不写回模板。
索引:prescriptions 顾客、接诊单;prescription_items 处方;herb_stock_logs 药材、类型。代煎领取与膏方领取流转已实装(两条独立单向链:待煎→可取→已取;待制作→可取→已取);药房发药已实装(dispense_status 0→1 单向 + dispense_logs 流水,代煎/膏方「已取」自动带出);饮片出入库流水已实装(余额按流水重算);配伍审方(十八反/十九畏)为静态规则比对(不落表),见 api 处方组。
staffs — 员工(登录主体,诊所运营)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| name | String | 姓名(非空) |
| account | String | 登录账号(唯一) |
| password | String | 密码(BCrypt) |
| phone | String | 手机号(唯一) |
| avatar | String | 头像 URL |
| age | Integer | 年龄 |
| gender | Integer | 性别(0 女、1 男) |
| address | String | 地址 |
| birthday | Date | 生日 |
| role | Integer | 角色:role == 0 返回 admin 权限集,1-9 为管理层(MANAGER 权限集),>= 10 为普通员工(USER,默认 99);方法级 @PreAuthorize 据此判定 |
| title | String | 职务(自由文本,可空):权限档 role 之外的职务语义与名册数据源,如 医师/针灸医师/前台;排班页「医师名册」= 在职(status=1)且职务含「医师」 |
| famous_level | Integer | 名医等级:0 普通(默认)、1-3 名医;仅影响列表排序(降序,名医在前)与标识徽章,不做阶梯/时段差异定价 |
| status | Integer | 状态:0 离职、1 在职、2 休假(<= 0 无法登录) |
索引:姓名、账号、手机号。
signs — 考勤打卡(诊所运营)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| staff_id | Long | 员工 id |
| type | Integer | 1 上班打卡、0 下班打卡 |
| create_time | Date | 打卡时间 |
考勤统计在查询时实时计算(无定时任务):同日多次上班卡取最早、下班卡取最晚,时长按整小时计。
staff_leaves — 员工请假(诊所运营)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| staff_id | Long | 员工 id(staffs.id) |
| leave_type | Integer | 类型:0 病假、1 事假 |
| reason | String | 事由(非空) |
| start_time / end_time | Date | 起止时间(结束不早于起始) |
| create_time / update_time | Date | 审计时间 |
索引:员工 id。口径:请假为记录性质,不改员工登录状态、无审批流(演示口径)。
staff_shifts — 员工周期班表(诊所运营)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| staff_id | Long | 员工 id(staffs.id) |
| weekday | Integer | 星期(1 周一 … 7 周日) |
| start_time / end_time | String | HH:mm 时段(start < end) |
| create_time / update_time | Date | 审计时间 |
索引:员工 id、星期。口径:同一员工同一星期仅一条班次,按周循环;班表只做排班参考,考勤打卡仍以 signs 为准(不回写打卡记录)。
ad_materials / ad_screens / ad_schedules — 广告投屏(平板展示)
配套 tablet/ Kiosk 端,设计见平板展示设计。文件本体不上库:素材上传落盘 data/ads/yyyyMM/(sinomed.ads.upload-dir 可改),表里存相对 url。
ad_materials — 素材:name、type(1 图片、2 视频)、url、duration_ms(轮播停留毫秒)、enabled、sort(顺序,小者在前);叠加层文案字段 category(分类)、kicker(眉标)、title(标题)、copy(正文)、price(价格,均可空)。
ad_screens — 屏:code(唯一,平板以 ?screen=code 打开)、name、location、enabled、last_seen_at(平板拉 playlist 时顺带心跳,管理端按 2 分钟阈值显示在线角标)。
ad_schedules — 排期:screen_id / material_id(业务外键)、weekdays(1=周一…7=周日 逗号分隔,空 = 每天)、start_time / end_time(HH:mm,空 = 全天)、enabled。
queue_calls — 叫号记录(平板展示)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键(同时作平板拉取游标 since) |
| screen_id | Long | 定向屏(业务外键;为空 = 全部屏广播) |
| number | String | 号码(如 08) |
| room | String | 诊室名 |
| patient_masked | String | 脱敏姓名(如 张*,隐私默认) |
| status | Integer | 0 待叫、1 已叫(当前写入即 1) |
| called_at | Date | 叫号时间 |
card_lookup_logs — 手机自助查询审计(查卡 + 查预约 + openid 绑定)
表名沿用查卡审计;/api/v1/kiosk/my-appointments(跨设备查自己的待到店预约)、/api/v1/kiosk/appointment-cancel(自助取消本人待到店预约,2026-10-16 代决)与 /api/v1/kiosk/openid-bind(openid 自助绑定)与查卡共用同一表,同一留存口径——身份验证后的顾客自助查询不留空。
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| phone | String | 被查卡项所属顾客手机号(openid 路径命中后回填;未命中为空) |
| operator | String | 查询者身份(谁查的):openid 路径与 openid 绑定=openid,短信路径=手机号 |
| mode | String | 生效模式(off / openid_only / sms_verified) |
| customer_id | Long | 命中的顾客 id(未命中为空) |
| result | String | 结果:ok / disabled / missing_openid / no_binding / missing_code / bad_code / expired_code / used_code / no_customer / bind_ok / bind_conflict(后两个为 openid 绑定路径) |
| create_time | Date | 时间 |
索引:phone、operator。口径:每次尝试(含拒绝)都落一条,查卡 / 查预约 / openid 绑定各路径同口径;见 kiosk.md。
sms_codes — 短信验证码(手机查卡短信路径)
| 字段 | 类型 | 含义 |
|---|---|---|
| id | Long | 主键 |
| phone | String | 手机号(非空) |
| code | String | 6 位数字码 |
| expires_at | Date | 过期时间(发码后 5 分钟) |
| used | Integer | 0 未用 / 1 已用(一次性,integer default 0) |
| attempts | Integer | 错试计数器(integer default 0);连续错满 5 次烧码(used=1,须重新取码)——口径见 api |
| create_time | Date | 发码时间 |
无真实短信网关:发码为服务端桩,码只落库与日志(SMS_CODE_DEMO phone=… code=…),测试从 SmsCodeRepository 直接取码。
wx_* — 微信公众号(模块表;设计稿 design/wechat-official-account.md,行为口径见 api wx 组)
公众号模块共 7 张表(列名逐字取自实体;均带 create_time 审计列,wx_message_logs 与 wx_binding_logs 为追加型、无 update_time,其余 5 表另有 update_time):
| 表 | 列 | 说明 |
|---|---|---|
wx_accounts | id, app_id(唯一), app_secret, token, aes_key, account_name, account_type(service/subscription), verified, encrypt_mode(plaintext/compatible/safe), enabled, remark | 配置入口(ADMIN 写入);app_secret/aes_key AES 加密落库(WX_OA_CREDENTIAL_KEY);token 为明文列;配置变更 evict 该账号的 SDK/路由缓存 |
wx_fans | id, account_id, openid, unionid, nickname, head_img_url, subscribe, subscribe_time, unsubscribe_time, customer_id, remark | 粉丝↔顾客绑定(customer_id 空=未绑);取关保留粉丝行、绝不自动解绑 |
wx_auto_replies | id, account_id, rule_type(subscribe/keyword/fallback), keyword, match_type(exact/contains), reply_type(text), content, priority, enabled | 每账号同时至多一条启用的 subscribe/fallback(服务层约束,无 DB 唯一);priority 数小者先 |
wx_menus | id, account_id, parent_id(两级), name, menu_type(9 个协议值), url, media_id, miniprogram_appid, miniprogram_pagepath, event_key, sort, enabled | 本地草稿;POST /wx/menus/push/{accountId} 推送启用项到微信(凭据门控);每账号 ≤3 顶级 × ≤5 二级 |
wx_materials | id, account_id, media_id, material_type(temp/image/video), name, url, local_path, size, mime_type | media_id 空=本地治理行(粉丝图捕获或本地上传);远端上传回写微信 media_id/url;(account_id, media_id) 唯一由启动期索引 uk_wx_material_account_media 建立 |
wx_message_logs | id, account_id, direction(in/out/reject), msg_type, from_user, to_user, content, msg_id, dedupe_key, result | 每条入站/出站/拒绝推送都落;result 覆盖 ok/disabled/account_missing/bad_signature/duplicate/…;disabled 期间的 reject 审计仍可查 |
wx_binding_logs | id, account_id, fan_openid, phone, customer_id, result | 粉丝绑定审计;result 覆盖 ok/missing_code/bad_code/used_code/expired_code/no_customer/bound_other/conflict |