外包风险评估系统:领域引擎+前端+服务端持久化与生产部署
- 确定性领域引擎(分类/评分/分级/红线/费用/裁决)+LLM(通义千问)语言理解 - 6步评估向导、服务端草稿持久化(跨设备/编辑草稿保护) - 工作流(草稿→风控→管理层)、RBAC、报告导出、校准、客户/费率/红线/最低工资管理 - 专业图标体系替换全部emoji、看板美化 - 生产化:API_BASE可配置(同源反代)、auth密钥惰性读取修复RBAC - 444单测+204前端测试+51 e2e
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 初始 schema:评估记录 / 工作流状态 / 操作记录 / 盈利分析。
|
||||
* 使用 IF NOT EXISTS 以与早期 initSchema 已建表的环境幂等兼容。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS assessments (
|
||||
id TEXT PRIMARY KEY,
|
||||
assessment JSONB NOT NULL,
|
||||
report JSONB,
|
||||
saved_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_assessments_saved_at ON assessments(saved_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS workflow_status (
|
||||
assessment_id TEXT PRIMARY KEY,
|
||||
status TEXT NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
assessment_id TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
comment TEXT,
|
||||
ts TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_assessment ON audit_logs(assessment_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS profitability (
|
||||
assessment_id TEXT PRIMARY KEY,
|
||||
result JSONB NOT NULL
|
||||
);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`
|
||||
DROP TABLE IF EXISTS profitability;
|
||||
DROP TABLE IF EXISTS audit_logs;
|
||||
DROP TABLE IF EXISTS workflow_status;
|
||||
DROP TABLE IF EXISTS assessments;
|
||||
`);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
/* eslint-disable */
|
||||
/** 综合承接建议持久化表。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS recommendations (
|
||||
assessment_id TEXT PRIMARY KEY,
|
||||
level TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
note TEXT NOT NULL,
|
||||
target_margin DOUBLE PRECISION NOT NULL,
|
||||
net_margin DOUBLE PRECISION,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS recommendations;`);
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
/* eslint-disable */
|
||||
/** 评估归档标记:归档项不在主列表显示,可单独查看。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE assessments ADD COLUMN IF NOT EXISTS archived BOOLEAN NOT NULL DEFAULT false;`);
|
||||
pgm.sql(`CREATE INDEX IF NOT EXISTS idx_assessments_archived ON assessments(archived);`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP INDEX IF EXISTS idx_assessments_archived;`);
|
||||
pgm.sql(`ALTER TABLE assessments DROP COLUMN IF EXISTS archived;`);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/* eslint-disable */
|
||||
/** 多报价方案对比:每个评估最多 5 套报价方案。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS scenarios (
|
||||
id TEXT NOT NULL,
|
||||
assessment_id TEXT NOT NULL REFERENCES assessments(id) ON DELETE CASCADE,
|
||||
label TEXT NOT NULL,
|
||||
inputs JSONB NOT NULL,
|
||||
result JSONB NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (assessment_id, id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_scenarios_assessment ON scenarios(assessment_id);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS scenarios;`);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable */
|
||||
/** 费率表后台化:消除硬编码,支持按地域/分类管理费率,版本化发布。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS rate_tables (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
region TEXT NOT NULL,
|
||||
category TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value DOUBLE PRECISION NOT NULL,
|
||||
effective_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
reviewed BOOLEAN NOT NULL DEFAULT false,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE(region, category, key, version)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_rate_tables_region ON rate_tables(region, category);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS rate_tables;`);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/* eslint-disable */
|
||||
/** 运营指标实际值回填(评估闭环)。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS actuals (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
assessment_id TEXT NOT NULL REFERENCES assessments(id) ON DELETE CASCADE,
|
||||
month INT NOT NULL,
|
||||
metric_name TEXT NOT NULL,
|
||||
actual_value DOUBLE PRECISION NOT NULL,
|
||||
recorded_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE(assessment_id, month, metric_name)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_actuals_assessment ON actuals(assessment_id);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS actuals;`);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable */
|
||||
/** 合规红线库(可配置):按地域/业务类型条件启用,版本化管理。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS redline_rules (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
trigger_condition TEXT NOT NULL,
|
||||
consequence TEXT NOT NULL,
|
||||
region TEXT,
|
||||
business_type TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
regulation_ref TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_redline_rules_region ON redline_rules(region);
|
||||
CREATE INDEX IF NOT EXISTS idx_redline_rules_biz ON redline_rules(business_type);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS redline_rules;`);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
/** 客户信用与集中度风险:客户档案表。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS customers (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
credit_rating TEXT NOT NULL DEFAULT '未评级',
|
||||
avg_overdue_days DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_contract_amount DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
assessment_count INT NOT NULL DEFAULT 0,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_customers_name ON customers(name);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS customers;`);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable */
|
||||
/** 评估有效期:默认 6 个月,到期可一键复评。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE assessments ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ;`);
|
||||
pgm.sql(`UPDATE assessments SET expires_at = saved_at + INTERVAL '6 months' WHERE expires_at IS NULL;`);
|
||||
pgm.sql(`CREATE INDEX IF NOT EXISTS idx_assessments_expires ON assessments(expires_at);`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP INDEX IF EXISTS idx_assessments_expires;`);
|
||||
pgm.sql(`ALTER TABLE assessments DROP COLUMN IF EXISTS expires_at;`);
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
/** 审批SLA:记录进入当前状态的时间,供超时计算。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE workflow_status ADD COLUMN IF NOT EXISTS entered_at TIMESTAMPTZ NOT NULL DEFAULT now();`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE workflow_status DROP COLUMN IF EXISTS entered_at;`);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
/* eslint-disable */
|
||||
/** 驳回原因结构化:枚举表 + 审计关联。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS reject_reasons (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
assessment_id TEXT NOT NULL,
|
||||
reason_type TEXT NOT NULL,
|
||||
detail TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_reject_reasons_assessment ON reject_reasons(assessment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_reject_reasons_type ON reject_reasons(reason_type);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS reject_reasons;`);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable */
|
||||
/** 全文检索:用 PG tsvector 做相似历史项目检索。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE assessments ADD COLUMN IF NOT EXISTS tsv tsvector;`);
|
||||
pgm.sql(`UPDATE assessments SET tsv = to_tsvector('simple', COALESCE(assessment->>'projectDescription','') || ' ' || COALESCE(assessment->>'businessType','') || ' ' || COALESCE(assessment->>'industry',''));`);
|
||||
pgm.sql(`CREATE INDEX IF NOT EXISTS idx_assessments_tsv ON assessments USING gin(tsv);`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP INDEX IF EXISTS idx_assessments_tsv;`);
|
||||
pgm.sql(`ALTER TABLE assessments DROP COLUMN IF EXISTS tsv;`);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
/** 附件管理:挂到评估/风险项,支持上传与审批时查证。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS attachments (
|
||||
id TEXT PRIMARY KEY,
|
||||
assessment_id TEXT NOT NULL REFERENCES assessments(id) ON DELETE CASCADE,
|
||||
risk_item_id TEXT,
|
||||
filename TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL DEFAULT 'application/octet-stream',
|
||||
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
storage_path TEXT NOT NULL,
|
||||
uploaded_by TEXT,
|
||||
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_attachments_assessment ON attachments(assessment_id);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS attachments;`);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable */
|
||||
/** LLM 经验库:沉淀综合研判 + 人工修正后的最终结论,供后续 few-shot。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS experience_library (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
assessment_id TEXT NOT NULL,
|
||||
business_type TEXT NOT NULL,
|
||||
industry TEXT NOT NULL,
|
||||
project_summary TEXT NOT NULL,
|
||||
risk_grade TEXT,
|
||||
acceptability TEXT,
|
||||
recommendation_level TEXT,
|
||||
lesson TEXT NOT NULL,
|
||||
tags TEXT[] NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_experience_biz ON experience_library(business_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_experience_tags ON experience_library USING gin(tags);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS experience_library;`);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable */
|
||||
/** 向量搜索:用 pgvector 存储项目描述的 embedding,做语义相似检索。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`CREATE EXTENSION IF NOT EXISTS vector;`);
|
||||
pgm.sql(`ALTER TABLE assessments ADD COLUMN IF NOT EXISTS embedding vector(1024);`);
|
||||
pgm.sql(`CREATE INDEX IF NOT EXISTS idx_assessments_embedding ON assessments USING ivfflat (embedding vector_cosine_ops) WITH (lists = 10);`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP INDEX IF EXISTS idx_assessments_embedding;`);
|
||||
pgm.sql(`ALTER TABLE assessments DROP COLUMN IF EXISTS embedding;`);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/* eslint-disable */
|
||||
/** 地域费率套:与引擎 RegionRates 结构对齐,复核后驱动评估盈利测算。 */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS region_rates (
|
||||
region TEXT PRIMARY KEY,
|
||||
rates JSONB NOT NULL,
|
||||
reviewed BOOLEAN NOT NULL DEFAULT false,
|
||||
updated_by TEXT,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS region_rates;`);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 红线可计算触发条件:将红线绑定到某项度量(月净利率/月毛利率/客户平均逾期天数/单客户集中度),
|
||||
* 配合比较运算符与阈值,使红线可在评估时自动判定命中(而非一律"待核实")。
|
||||
*
|
||||
* - linked_metric: 关联度量键(netMargin / grossMargin / avgOverdueDays / concentration),
|
||||
* 为空表示该红线仍需人工核实(合规/资质类红线)。
|
||||
* - compare_op: 比较运算符(>=, <=, >, <)。
|
||||
* - threshold: 阈值;百分比类度量(净利率/毛利率/集中度)按百分数填写(如 0 表示 0%、5 表示 5%),
|
||||
* 逾期天数按天填写。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE redline_rules ADD COLUMN IF NOT EXISTS linked_metric TEXT;
|
||||
ALTER TABLE redline_rules ADD COLUMN IF NOT EXISTS compare_op TEXT;
|
||||
ALTER TABLE redline_rules ADD COLUMN IF NOT EXISTS threshold DOUBLE PRECISION;
|
||||
`);
|
||||
|
||||
// 为内置数值型红线预置可计算条件(仅当字段为空时设置,避免覆盖人工配置)。
|
||||
pgm.sql(`
|
||||
UPDATE redline_rules SET linked_metric='netMargin', compare_op='<', threshold=0
|
||||
WHERE id='negative-margin-3m' AND linked_metric IS NULL;
|
||||
UPDATE redline_rules SET linked_metric='avgOverdueDays', compare_op='>', threshold=90
|
||||
WHERE id='overdue-90days' AND linked_metric IS NULL;
|
||||
UPDATE redline_rules SET linked_metric='concentration', compare_op='>', threshold=50
|
||||
WHERE id='concentration-50pct' AND linked_metric IS NULL;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE redline_rules DROP COLUMN IF EXISTS linked_metric;
|
||||
ALTER TABLE redline_rules DROP COLUMN IF EXISTS compare_op;
|
||||
ALTER TABLE redline_rules DROP COLUMN IF EXISTS threshold;
|
||||
`);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 保存盈利测算的原始输入(报价模式/合同周期/成本加成率/各项成本参数/岗位明细等),
|
||||
* 以便编辑评估时完整带入原值(此前仅保存计算结果,导致加成率等输入项丢失)。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE profitability ADD COLUMN IF NOT EXISTS inputs JSONB;`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`ALTER TABLE profitability DROP COLUMN IF EXISTS inputs;`);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 红线复合条件:在主条件之外,支持一个可选的第二条件(与主条件 AND 组合)。
|
||||
* 例如「月毛利率 < 0 且 月净利率 < 0」同时满足才命中。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE redline_rules ADD COLUMN IF NOT EXISTS linked_metric2 TEXT;
|
||||
ALTER TABLE redline_rules ADD COLUMN IF NOT EXISTS compare_op2 TEXT;
|
||||
ALTER TABLE redline_rules ADD COLUMN IF NOT EXISTS threshold2 DOUBLE PRECISION;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE redline_rules DROP COLUMN IF EXISTS linked_metric2;
|
||||
ALTER TABLE redline_rules DROP COLUMN IF EXISTS compare_op2;
|
||||
ALTER TABLE redline_rules DROP COLUMN IF EXISTS threshold2;
|
||||
`);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 应用级设置(键值):用于持久化可调参数,如目标净利率基准(由预测准确度校准)。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS app_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value DOUBLE PRECISION NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS app_settings;`);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 客户回款记录:记录应收发票的到期日与实际回款日,用于自动计算客户平均逾期天数,
|
||||
* 替代人工维护的 avg_overdue_days,并驱动"客户逾期超N天"红线。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS customer_payments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
customer_id TEXT NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
|
||||
invoice_amount DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
due_date DATE NOT NULL,
|
||||
paid_date DATE,
|
||||
note TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_customer_payments_cust ON customer_payments(customer_id);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS customer_payments;`);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 各地域月最低工资标准(可后台维护):驱动"低于最低工资"红线自动比对。
|
||||
* 预置近似默认值,须经 HR/财务按当地官方标准复核更新。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS min_wages (
|
||||
region TEXT PRIMARY KEY,
|
||||
monthly_wage DOUBLE PRECISION NOT NULL,
|
||||
updated_by TEXT,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
INSERT INTO min_wages(region, monthly_wage) VALUES
|
||||
('北京', 2420), ('上海', 2690), ('广东', 1900), ('深圳', 2360),
|
||||
('江苏', 2490), ('浙江', 2490), ('四川', 2100), ('河北', 2200), ('中国大陆', 2000)
|
||||
ON CONFLICT(region) DO NOTHING;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS min_wages;`);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 评估向导草稿(服务端持久化,跨设备):保存未运行/未提交的向导填写进度。
|
||||
* - source_assessment_id 为空:全新建评估的草稿
|
||||
* - source_assessment_id 非空:编辑某既有评估时的草稿(中断不丢失)
|
||||
* form 存完整向导快照(步骤/立项/描述/业务类型/指标作答/岗位/成本参数等)。
|
||||
*/
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
CREATE TABLE IF NOT EXISTS wizard_drafts (
|
||||
id TEXT PRIMARY KEY,
|
||||
assessor_id TEXT,
|
||||
source_assessment_id TEXT,
|
||||
project_name TEXT,
|
||||
form JSONB NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wizard_drafts_assessor ON wizard_drafts(assessor_id);
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`DROP TABLE IF EXISTS wizard_drafts;`);
|
||||
};
|
||||
Reference in New Issue
Block a user