Files
yxe-java/schema_all_2026-10-11.sql
weicw-dshsa 1c431c6899 chore(db): 合并数据库变更为单一全量脚本 schema_all_2026-10-11.sql
原 yxe-java/ 根目录散落 12 个 SQL 文件,同一张表的建表与加列分散在多处,
后续只改一处容易漏。合并为单文件,各变更的「用途/背景/影响/性质/配套代码」
头注与执行前后核对 SELECT 全部保留,末尾附执行记录。

涵盖:yxe_self_report(+activity_time/reject_reason)、yxe_visit_report、
yxe_visit_list(+visit_type/visit_date/submitter_name)、yxe_user_task.income、
sys_user.user_group、yxe_chronic_disease_product、transport_maintenance_*,
以及两项历史数据修正(chain_id 占位符、yxe_task.reward NULL)。

一并修复:原脚本在全新库按序执行会报错
  yxe_self_report.sql 已内联 activity_time/reject_reason,而
  yxe_self_report_add_* 两个裸 ALTER 又去 ADD 同名列
  → ERROR 1060 Duplicate column name
现全部加列改为 information_schema 判断后再 ADD,整文件可重复执行。
另为第 6/7 节补 SET @db(原先依赖第 2 节的变量,逐语句执行时会丢)。

验证(在独立临时库 yxe_schema_verify,未触碰 yaoxiaoer):
- 整文件连续执行两遍,均 exit=0,无 error / Duplicate,7 处正确跳过
- 产出结构与 yaoxiaoer 逐表 SHOW CREATE TABLE 比对,9 张表全部一致
- 另建库复现原缺陷,确认裸 ALTER 报 ERROR 1060

注:yxe_task.reward 的 NULL 修正(第 10 节)在本地库尚未执行,
    id=663 仍为 NULL;接口已因读侧兜底不再报错,如需数据一致请执行该节。
2026-10-11 22:58:04 +08:00

611 lines
33 KiB
SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================================
-- yxe 库结构与数据修正 —— 全量累计脚本
-- ============================================================================
-- 用途:yxe 业务库(yaoxiaoer)的建表 / 加列 / 历史数据修正,汇总为一个可整
-- 文件重复执行的脚本,供新环境初始化与生产库复核执行。
-- 日期:2026-10-11
-- 背景:原先这些变更散落在 yxe-java/ 根目录的 12 个独立 SQL 文件中,同一张表
-- 的建表与加列分散在多处,后续维护容易只改一处;本次合并为单文件,
-- 原文件内容完整保留在本文件对应分节中。
--
-- 由以下 12 个文件合并而来(合并后原文件删除,历史可经 git log 追溯):
-- yxe_self_report.sql
-- yxe_self_report_add_activity_time.sql
-- yxe_self_report_add_reject_reason.sql
-- yxe_visit_report.sql (2026-10-11 新增)
-- yxe_visit_list_add_visit_form_fields_2026-10-11.sql(2026-10-11 新增)
-- yxe_user_task_add_income.sql
-- yxe_chronic_disease_product.sql
-- sys_user_add_user_group.sql
-- transport_maintenance_plan.sql
-- transport_maintenance_record.sql
-- fix_self_report_chain_id_placeholder_2026-10-11.sql(2026-10-11 数据修正)
-- fix_task_reward_null_npe_2026-10-11.sql (2026-10-11 数据修正)
--
-- 性质:整体幂等,可整文件重复执行。
-- 建表用 CREATE TABLE IF NOT EXISTS;加列统一走 information_schema 判断后
-- 再 ADD(原先部分 ALTER 是裸语句,重复执行或与建表语句叠加时会报
-- Duplicate column name,本次一并改为条件执行);数据修正限定条件,
-- 重复执行不改变结果。
--
-- 执行顺序:按本文件自上而下执行即可。分节顺序已考虑依赖:
-- 先建表/加列(第 1–8 节),最后做历史数据修正(第 9–10 节)。
-- 注意第 1 节的建表已内联 activity_time / reject_reason,
-- 第 2–3 节的加列会自动跳过(已存在)。
--
-- 配套代码(各变更的详细说明见对应分节):
-- yxe-java YxeSelfReport* / YxeVisitReport* / YxeVisitList* / YxeTask* /
-- YxeUserTask* / YxeChronicDiseaseProduct* 及对应 Controller
-- yxe-mp packageUser/self-report/*、pages/visit/index、pages/audit/index
-- yxe-admin src/api/yxe/*、src/views/yxe/*
-- ============================================================================
-- ############################################################################
-- 1. yxe_self_report —— 自主填报(驻店填报)建表
-- ############################################################################
-- 用途:自主填报(2026-10 起对外称「驻店填报」)主表
-- 背景:自主填报功能的原始建表语句;后续新增的 activity_time / reject_reason
-- 两列已内联到本建表语句中(见下方列定义),因此第 2–3 节的加列在
-- 新库上会走「已存在则跳过」分支。
-- 影响:新库建表 0 行;已存在的库不做任何改动。
-- 性质:幂等(CREATE TABLE IF NOT EXISTS)。
-- 配套代码:YxeSelfReport 实体与 YxeSelfReportController 全套。
-- 执行前核对(应为 0,即表不存在)
SELECT COUNT(*) AS table_exists
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_self_report';
CREATE TABLE IF NOT EXISTS `yxe_self_report` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int DEFAULT NULL COMMENT '提交人ID',
`merchant_id` int DEFAULT NULL COMMENT '门店ID',
`merchant_name` varchar(255) DEFAULT NULL COMMENT '门店名称',
`chain_id` varchar(64) DEFAULT NULL COMMENT '连锁ID',
`chain_name` varchar(255) DEFAULT NULL COMMENT '连锁名称',
`date` varchar(32) DEFAULT NULL COMMENT '填报日期',
`activity_time` varchar(32) DEFAULT NULL COMMENT '活动时间',
`reward` decimal(10,2) DEFAULT NULL COMMENT '奖金金额',
`image` longtext COMMENT '全部图片集合',
`clock_photos` longtext COMMENT '上下班打卡图',
`booth_photos` longtext COMMENT '门头摆台照',
`sales_groups` longtext COMMENT '销量填写JSON',
`receipt_photo` longtext COMMENT '小票图',
`summary` longtext COMMENT '活动小结',
`rating` varchar(32) DEFAULT NULL COMMENT '门店评分',
`lat` varchar(64) DEFAULT NULL COMMENT '纬度',
`lng` varchar(64) DEFAULT NULL COMMENT '经度',
`location` varchar(500) DEFAULT NULL COMMENT '定位地址',
`sort_number` int DEFAULT 0 COMMENT '排序',
`comments` varchar(500) DEFAULT NULL COMMENT '备注',
`status` tinyint DEFAULT 0 COMMENT '状态:0待审核,1通过,2拒绝',
`reject_reason` varchar(500) DEFAULT NULL COMMENT '驳回原因',
`deleted` tinyint DEFAULT 0 COMMENT '逻辑删除',
`tenant_id` int DEFAULT 10325 COMMENT '租户ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_yxe_self_report_user_id` (`user_id`),
KEY `idx_yxe_self_report_merchant_id` (`merchant_id`),
KEY `idx_yxe_self_report_status` (`status`),
KEY `idx_yxe_self_report_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自主填报';
-- 执行后核对(应返回 1 行,且列/注释符合预期)
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_self_report'
ORDER BY ORDINAL_POSITION;
-- ############################################################################
-- 2. yxe_self_report —— 新增 activity_time(活动时间)
-- ############################################################################
-- 用途:自主填报新增「活动时间」
-- 背景:申报活动时间需要与填报日期(date)区分开,单独一列留存。
-- 注意:该列已内联进第 1 节的建表语句,本节的加列在新库上会跳过,
-- 保留此节是为兼容「先有旧表、后加列」的存量库。
-- 影响:ALTER 本身 0 行;存量数据该列为 NULL,不做回填。
-- 性质:幂等(information_schema 判断列是否存在后 ADD)。
-- 配套代码:YxeSelfReport.activityTime、yxe-mp create.vue 活动日期选择器。
-- 执行前核对(应为 0,即列不存在)
SELECT COUNT(*) AS col_exists
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_self_report'
AND COLUMN_NAME = 'activity_time';
SET @db := DATABASE();
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yxe_self_report' AND COLUMN_NAME = 'activity_time') = 0,
'ALTER TABLE `yxe_self_report` ADD COLUMN `activity_time` varchar(32) DEFAULT NULL COMMENT ''活动时间'' AFTER `date`',
'SELECT ''yxe_self_report.activity_time already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ############################################################################
-- 3. yxe_self_report —— 新增 reject_reason(驳回原因)
-- ############################################################################
-- 用途:自主填报新增「驳回原因」
-- 背景:审核端驳回后需要把原因回传给提交人,并在重新提交时展示。
-- 注意:该列已内联进第 1 节的建表语句,本节同样是为存量库兼容保留。
-- 影响:ALTER 本身 0 行;存量数据该列为 NULL。
-- 性质:幂等(information_schema 判断列是否存在后 ADD)。
-- 配套代码:YxeSelfReport.rejectReason、YxeSelfReportController#resubmit。
-- 执行前核对(应为 0,即列不存在)
SELECT COUNT(*) AS col_exists
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_self_report'
AND COLUMN_NAME = 'reject_reason';
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yxe_self_report' AND COLUMN_NAME = 'reject_reason') = 0,
'ALTER TABLE `yxe_self_report` ADD COLUMN `reject_reason` varchar(500) DEFAULT NULL COMMENT ''驳回原因'' AFTER `status`',
'SELECT ''yxe_self_report.reject_reason already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 执行后核对(第 2–3 节合计应为 2 行)
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_self_report'
AND COLUMN_NAME IN ('activity_time', 'reject_reason')
ORDER BY ORDINAL_POSITION;
-- ############################################################################
-- 4. yxe_visit_report —— 跑店填报建表
-- ############################################################################
-- 用途:新增 yxe_visit_report 表(跑店填报)
-- 背景:甲方要求把「自主填报」拆成「驻店填报 / 跑店填报」两种类型:
-- 1) 原自主填报页改为「驻店填报」,数据仍存 yxe_self_report,不动;
-- 2) 新增「跑店填报」,填报内容为「巡店列表(多门店,每条含门店/定位/打卡照片)
-- + 活动小结」。
-- 巡店列表是「一次填报多家门店」的数组结构,yxe_self_report 既没有多门店
-- 字段,也没有区分驻店/跑店类型的字段;本次选择新建独立表,避免污染既有
-- 历史数据与既有审核/导出逻辑。
-- 影响:新建空表,0 行业务数据;不改动 yxe_self_report 任何结构。
-- 性质:幂等(CREATE TABLE IF NOT EXISTS)。
-- 配套代码:
-- yxe-java YxeVisitReport.java / YxeVisitReportParam.java / YxeVisitReportMapper.java
-- / YxeVisitReportMapper.xml / YxeVisitReportService(+Impl)
-- / YxeVisitReportController.java
-- yxe-mp packageUser/self-report/select.vue(选择填报类型)
-- packageUser/self-report/visit.vue(跑店填报)
-- packageUser/self-report/records.vue(填报记录加类型切换)
-- pages/audit/index.vue(药审核加跑店填报类型)
-- yxe-admin src/api/yxe/yxeVisitReport/*、src/views/yxe/yxeSelfReport/*(类型筛选与跑店详情)
-- 执行前核对(应为 0,即表不存在)
SELECT COUNT(*) AS table_exists
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_visit_report';
-- 变更:建表(已存在则跳过)
CREATE TABLE IF NOT EXISTS `yxe_visit_report` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int DEFAULT NULL COMMENT '提交人ID',
`store_count` int DEFAULT 0 COMMENT '巡店门店数',
`store_list` longtext COMMENT '巡店列表JSON(门店/定位/打卡照片)',
`image` longtext COMMENT '全部图片集合',
`summary` longtext COMMENT '活动小结',
`reward` decimal(10,2) DEFAULT NULL COMMENT '奖金金额(审核时填写)',
`lat` varchar(64) DEFAULT NULL COMMENT '纬度(整单,取首条门店)',
`lng` varchar(64) DEFAULT NULL COMMENT '经度(整单,取首条门店)',
`location` varchar(500) DEFAULT NULL COMMENT '定位地址(整单,取首条门店)',
`sort_number` int DEFAULT 0 COMMENT '排序',
`comments` varchar(500) DEFAULT NULL COMMENT '备注',
`status` tinyint DEFAULT 0 COMMENT '状态:0待审核,1通过,2拒绝',
`reject_reason` varchar(500) DEFAULT NULL COMMENT '驳回原因',
`deleted` tinyint DEFAULT 0 COMMENT '逻辑删除',
`tenant_id` int DEFAULT 10325 COMMENT '租户ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_yxe_visit_report_user_id` (`user_id`),
KEY `idx_yxe_visit_report_status` (`status`),
KEY `idx_yxe_visit_report_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='跑店填报';
-- 执行后核对(应返回 18 行,且列/注释符合预期)
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_visit_report'
ORDER BY ORDINAL_POSITION;
-- ############################################################################
-- 5. yxe_visit_list —— 新增客户拜访表单字段
-- ############################################################################
-- 用途:yxe_visit_list 表新增客户拜访表单字段(拜访对象类型、拜访日期、提交人姓名)
-- 背景:小程序 pages/visit/index 按「客户拜访」新原型重构为多卡片表单:
-- 1) 新增「拜访对象 - 类型」(地点 / 门店经理 / 其他);
-- 2) 新增「拜访信息 - 拜访日期」,默认当天;
-- 3) 新增「我的信息 - 姓名」,进页面自动带出账号 real_name,仍允许修改,
-- 因此与 sys_user.real_name 可能不一致,需要独立字段留痕。
-- 三项均为本次新增,历史记录该字段为空(前端展示为 "-")。
-- 影响:ALTER 语句本身 0 行;已有历史数据新增列为 NULL,不做回填。
-- 性质:幂等(通过 information_schema 判断列是否存在后再 ADD)。
-- 配套代码:
-- yxe-java YxeVisitList.java / YxeVisitListParam.java / YxeVisitListMapper.xml
-- / YxeVisitListController.java(导出表头)
-- yxe-mp pages/visit/index.vue
-- yxe-admin src/api/yxe/yxeVisitList/model、列表列、详情、编辑弹窗
-- 备注:本列注释在源码中已由「门店/门店经理/其他」改为「地点/门店经理/其他」。
-- 对**已存在该列**的库(列已建、注释仍是旧值),本节按幂等设计跳过,
-- 不会修正注释;如需同步注释,另行执行:
-- ALTER TABLE `yxe_visit_list` MODIFY COLUMN `visit_type` varchar(16)
-- DEFAULT NULL COMMENT '拜访对象类型: 地点/门店经理/其他';
-- 新库建列时直接用下面语句,注释即为新值。
-- 执行前核对(应为 0 行,即三列均不存在)
SELECT COLUMN_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_visit_list'
AND COLUMN_NAME IN ('visit_type', 'visit_date', 'submitter_name');
-- 变更:按列分别判断,已存在则跳过
SET @db := DATABASE();
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yxe_visit_list' AND COLUMN_NAME = 'visit_type') = 0,
'ALTER TABLE `yxe_visit_list` ADD COLUMN `visit_type` varchar(16) DEFAULT NULL COMMENT ''拜访对象类型: 地点/门店经理/其他'' AFTER `name`',
'SELECT ''visit_type already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yxe_visit_list' AND COLUMN_NAME = 'visit_date') = 0,
'ALTER TABLE `yxe_visit_list` ADD COLUMN `visit_date` varchar(32) DEFAULT NULL COMMENT ''拜访日期'' AFTER `visit_type`',
'SELECT ''visit_date already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yxe_visit_list' AND COLUMN_NAME = 'submitter_name') = 0,
'ALTER TABLE `yxe_visit_list` ADD COLUMN `submitter_name` varchar(64) DEFAULT NULL COMMENT ''提交人姓名(拜访人自填)'' AFTER `visit_date`',
'SELECT ''submitter_name already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 执行后核对(应为 3 行,且类型/注释符合预期)
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_visit_list'
AND COLUMN_NAME IN ('visit_type', 'visit_date', 'submitter_name')
ORDER BY ORDINAL_POSITION;
-- ############################################################################
-- 6. yxe_user_task —— 新增 income(任务收入)
-- ############################################################################
-- 用途:用户任务新增「任务收入」
-- 背景:任务完成后的收入需要单独留存,便于业绩统计与提成核算。
-- 影响:ALTER 本身 0 行;存量数据该列为 NULL,不做回填。
-- 性质:幂等(information_schema 判断列是否存在后 ADD)。
-- 配套代码:YxeUserTask.income、yxe-mp pages/bonus/index。
-- 执行前核对(应为 0,即列不存在)
SELECT COUNT(*) AS col_exists
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_user_task'
AND COLUMN_NAME = 'income';
SET @db := DATABASE();
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'yxe_user_task' AND COLUMN_NAME = 'income') = 0,
'ALTER TABLE `yxe_user_task` ADD COLUMN `income` decimal(10, 2) NULL DEFAULT NULL COMMENT ''任务收入'' AFTER `chain`',
'SELECT ''yxe_user_task.income already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 执行后核对(应为 1 行)
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yxe_user_task'
AND COLUMN_NAME = 'income';
-- ############################################################################
-- 7. sys_user —— 新增 user_group(用户分组)
-- ############################################################################
-- 用途:系统用户新增「用户分组」
-- 背景:审核与导出需要按用户分组筛选(yxe_self_report / yxe_visit_report 的
-- userGroup 查询条件即走本列)。
-- 影响:ALTER 本身 0 行;存量用户该列为 NULL。
-- 性质:幂等(information_schema 判断列是否存在后 ADD)。
-- 配套代码:YxeSelfReportMapper.xml / YxeVisitReportMapper.xml 的 userGroup 条件、
-- yxe-admin 自主填报列表「用户分组」筛选项。
-- 执行前核对(应为 0,即列不存在)
SELECT COUNT(*) AS col_exists
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sys_user'
AND COLUMN_NAME = 'user_group';
SET @db := DATABASE();
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'sys_user' AND COLUMN_NAME = 'user_group') = 0,
'ALTER TABLE sys_user ADD COLUMN user_group VARCHAR(50) NULL COMMENT ''用户分组'' AFTER user_level',
'SELECT ''sys_user.user_group already exists'' AS skipped'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 执行后核对(应为 1 行)
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT, ORDINAL_POSITION
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sys_user'
AND COLUMN_NAME = 'user_group';
-- ############################################################################
-- 8. 其余建表:慢病产品 / 运输保养 / 运输维修
-- ############################################################################
-- === 8.1 yxe_chronic_disease_product —— 慢病产品管理 =========================
-- 用途:慢病产品基础资料(连锁 / 产品名 / 规格粒数 / 每日用量 / 每盒天数等)
-- 影响:新库建表 0 行。
-- 性质:幂等(CREATE TABLE IF NOT EXISTS)。
CREATE TABLE IF NOT EXISTS `yxe_chronic_disease_product` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`chain` varchar(100) NOT NULL COMMENT '连锁',
`product_name` varchar(100) NOT NULL COMMENT '产品名',
`spec_count` decimal(10,2) NOT NULL COMMENT '规格粒/片',
`daily_usage_count` decimal(10,2) NOT NULL COMMENT '说明书每天使用/次',
`dosage_per_time` decimal(10,2) NOT NULL COMMENT '每次几粒/片',
`box_usage_days` decimal(10,2) DEFAULT NULL COMMENT '1盒服用几天',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态, 0禁用, 1启用',
`deleted` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`tenant_id` int DEFAULT NULL COMMENT '租户id',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_ycdp_product_name` (`product_name`),
KEY `idx_ycdp_chain` (`chain`),
KEY `idx_ycdp_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='慢病产品管理';
-- === 8.2 transport_maintenance_plan —— 保养记录 ============================
-- 用途:车辆/船舶保养记录
-- 影响:新库建表 0 行。
-- 性质:幂等(CREATE TABLE IF NOT EXISTS)。
CREATE TABLE IF NOT EXISTS `transport_maintenance_plan` (
`maintenance_plan_id` int NOT NULL AUTO_INCREMENT COMMENT '保养记录ID',
`vehicle_type` varchar(10) NOT NULL COMMENT '车船类型: 车辆, 船舶',
`vehicle_no` varchar(30) NOT NULL COMMENT '车牌号/船号',
`maintainer` varchar(20) DEFAULT NULL COMMENT '保养人',
`maintenance_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '保养时间',
`mileage` decimal(12,2) DEFAULT NULL COMMENT '里程/航程数',
`mileage_unit` varchar(10) DEFAULT NULL COMMENT '里程/航程单位: 公里, 海里',
`maintenance_item` varchar(100) DEFAULT NULL COMMENT '保养项目',
`cost` decimal(12,2) NOT NULL DEFAULT 0.00 COMMENT '费用',
`store_name` varchar(50) DEFAULT NULL COMMENT '店名',
`contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`address` varchar(100) DEFAULT NULL COMMENT '地址',
`next_maintenance_time` datetime DEFAULT NULL COMMENT '下次保养时间',
`next_maintenance_mileage` decimal(12,2) DEFAULT NULL COMMENT '下次保养里程/航程',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`attachments` longtext COMMENT '附件, JSON或逗号分隔文件地址',
`sort_number` int NOT NULL DEFAULT 0 COMMENT '排序(数字越小越靠前)',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '状态, 0正常, 1禁用',
`deleted` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`user_id` int DEFAULT NULL COMMENT '创建人ID',
`organization_id` int DEFAULT NULL COMMENT '组织ID',
`tenant_id` int DEFAULT 10325 COMMENT '租户ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`maintenance_plan_id`),
KEY `idx_tmp_vehicle_type` (`vehicle_type`),
KEY `idx_tmp_vehicle_no` (`vehicle_no`),
KEY `idx_tmp_maintenance_time` (`maintenance_time`),
KEY `idx_tmp_next_maintenance_time` (`next_maintenance_time`),
KEY `idx_tmp_organization_id` (`organization_id`),
KEY `idx_tmp_tenant_id` (`tenant_id`),
KEY `idx_tmp_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='保养记录';
-- === 8.3 transport_maintenance_record —— 维修记录 ==========================
-- 用途:车辆/船舶维修记录
-- 影响:新库建表 0 行。
-- 性质:幂等(CREATE TABLE IF NOT EXISTS)。
CREATE TABLE IF NOT EXISTS `transport_maintenance_record` (
`maintenance_record_id` int NOT NULL AUTO_INCREMENT COMMENT '维修记录ID',
`vehicle_type` varchar(10) NOT NULL COMMENT '车船类型: 车辆, 船舶',
`vehicle_no` varchar(30) NOT NULL COMMENT '车牌号/船号',
`maintainer` varchar(20) DEFAULT NULL COMMENT '维修人',
`maintenance_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '维修时间',
`location` varchar(100) DEFAULT NULL COMMENT '维修位置',
`replaced_part` varchar(200) DEFAULT NULL COMMENT '更换零件',
`cost` decimal(12,2) NOT NULL DEFAULT 0.00 COMMENT '费用',
`company` varchar(50) DEFAULT NULL COMMENT '维修单位',
`contact` varchar(20) DEFAULT NULL COMMENT '联系方式',
`address` varchar(100) DEFAULT NULL COMMENT '地址',
`factory_time` datetime DEFAULT NULL COMMENT '出厂时间',
`mileage` decimal(12,2) DEFAULT NULL COMMENT '里程/航程数',
`mileage_unit` varchar(10) DEFAULT NULL COMMENT '里程/航程单位: 公里, 海里',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`attachments` longtext COMMENT '附件, JSON或逗号分隔文件地址',
`sort_number` int NOT NULL DEFAULT 0 COMMENT '排序(数字越小越靠前)',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '状态, 0正常, 1禁用',
`deleted` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`user_id` int DEFAULT NULL COMMENT '创建人ID',
`organization_id` int DEFAULT NULL COMMENT '组织ID',
`tenant_id` int DEFAULT 10325 COMMENT '租户ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`maintenance_record_id`),
KEY `idx_tmr_vehicle_type` (`vehicle_type`),
KEY `idx_tmr_vehicle_no` (`vehicle_no`),
KEY `idx_tmr_maintenance_time` (`maintenance_time`),
KEY `idx_tmr_factory_time` (`factory_time`),
KEY `idx_tmr_organization_id` (`organization_id`),
KEY `idx_tmr_tenant_id` (`tenant_id`),
KEY `idx_tmr_deleted` (`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='维修记录';
-- 执行后核对(三张表应各返回 1 行)
SELECT TABLE_NAME, TABLE_COMMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME IN ('yxe_chronic_disease_product',
'transport_maintenance_plan',
'transport_maintenance_record')
ORDER BY TABLE_NAME;
-- ############################################################################
-- 9. 数据修正:yxe_self_report.chain_id 残留位置型占位符
-- ############################################################################
-- 用途:修正 yxe_self_report.chain_id 中残留的位置型占位符(chain_0、chain_3、chain_6 …)
-- 背景:前端 normalizeMerchantChain 在接口未返回 chainId 时会回退成 `chain_${index}`,
-- 该值经审核端 saveMerchantEdit 写入 chain_id 落库。而按连锁筛选走的是等值匹配
-- (YxeSelfReportMapper.xml: a.chain_id = #{param.chainId}),占位符导致这些记录
-- 在按连锁筛选时被永久漏掉。本系统没有连锁表,chain 名称即事实上的连锁标识
-- (连锁字典 sys_dict_data.dict_id=175 的 dict_data_code 同样是连锁名)。
-- 影响:本次实际 5 行(chain_6 -> 老百姓 3 行;chain_3 -> 百姓人家 2 行)。
-- 可重复执行:修正后不再匹配 WHERE 条件,无副作用。
-- 性质:幂等(以 chain_name 为准回填 chain_id,限定 REGEXP 占位符)。
-- 配套代码:MerchantServiceImpl.groupByChain 返回稳定 chainId;
-- AuditSelfReportItem.vue 兜底链改用连锁名并在写入前拦截 chain_ 前缀。
-- 执行前核对(本地库曾为 5 行;执行后应为 0 行)
SELECT id, chain_id, chain_name
FROM yxe_self_report
WHERE chain_id REGEXP '^chain_[0-9]+$'
ORDER BY id;
-- 修正
UPDATE yxe_self_report
SET chain_id = chain_name
WHERE chain_id REGEXP '^chain_[0-9]+$'
AND chain_name IS NOT NULL
AND chain_name <> '';
-- 执行后核对(remaining_placeholder 与 mismatch 均应为 0)
SELECT SUM(chain_id REGEXP '^chain_[0-9]+$') AS remaining_placeholder,
SUM(chain_id <> chain_name) AS mismatch,
COUNT(*) AS total
FROM yxe_self_report;
-- ############################################################################
-- 10. 数据修正:yxe_task.reward 为 NULL 的历史脏数据
-- ############################################################################
-- 用途:把 yxe_task.reward 为 NULL 的历史脏数据规范化为 0,消除业绩统计接口的空指针隐患
-- 背景:小程序 pages/performance/index 页面请求的 /yxe-user-task/userRank 与 /yxe-task/data
-- 同时返回 {"code":1,"message":"操作失败","error":"java.lang.NullPointerException"},
-- 页面「我的完成进度/完成率」与人员排行榜全部空白。
-- 根因:yxe_task.reward 为 NULL 的任务被计入完成数后执行
-- BigDecimal.ZERO.add(task.getReward())
-- → BigDecimal.add(null) 抛 NPE,整个接口失败。
-- 本库命中数据为 yxe_task.id=663(title='测试手动',status=2 已完成,
-- 其 yxe_user_task.id=630 / user_id=875 的 status=2)。注意任务状态为 2 的只有这 1 条,
-- 但它足以让「全量排行榜」接口整体失败——userRank 不传 name 时遍历全部用户。
-- 影响:1 行。
-- 性质:幂等(重复执行时 WHERE 不再命中)。
-- 配套代码:
-- 1) 读侧兜底:YxeTaskController#data、YxeUserTaskController#userRank
-- 对 reward 做 nullToZero,接口不再报错;
-- 2) 写侧堵源:YxeTaskController#save / #saveBatch 在新增时把缺失的 reward 兜底为 0,
-- 不再产生新的 NULL 脏数据。
-- 注意 #update / #updateBatch **刻意不补默认值**:二者是部分更新语义
-- (MyBatis-Plus 默认 NOT_NULL 策略),null 表示“本次不修改该字段”,
-- 若在那里兜底会把任务原有奖金静默清零,直接影响用户实得收益。
-- 3) 回归测试:src/test/java/com/gxwebsoft/yxe/controller/TaskRewardNullSafetyTest.java
-- 说明:本脚本属于「历史数据规范化」。即使不执行,接口也已不再报错(读侧兜底),
-- 但金额会按 0 计;执行后数据本身也归于一致。
-- 执行前核对:先看清 NULL 分布在哪些任务、是否已有人完成任务
SELECT t.id AS task_id,
t.title,
t.status AS task_status,
t.reward,
COUNT(ut.id) AS user_task_cnt,
SUM(ut.status = 2) AS completed_cnt
FROM yxe_task t
LEFT JOIN yxe_user_task ut ON ut.task_id = t.id
WHERE t.reward IS NULL
GROUP BY t.id, t.title, t.status, t.reward
ORDER BY t.id;
-- 变更:仅规范化「已完成/进行中」且 reward 为 NULL 的任务,避免影响尚未开始的任务
UPDATE yxe_task
SET reward = 0.00
WHERE reward IS NULL
AND status IN (1, 2);
-- 执行后核对(remaining_null 应为 0)
SELECT SUM(reward IS NULL) AS remaining_null,
SUM(reward IS NULL AND status IN (1, 2)) AS remaining_null_active,
COUNT(*) AS total
FROM yxe_task;
-- ============================================================================
-- 执行记录
-- ============================================================================
-- 2026-10-11 本地开发库(127.0.0.1:3306/yaoxiaoer,root)
-- 第 1 节 yxe_self_report 建表 —— 表已存在,跳过
-- 第 2 节 activity_time —— 列已存在,跳过
-- 第 3 节 reject_reason —— 列已存在,跳过
-- 第 4 节 yxe_visit_report 建表 —— 已执行(18 列,0 行业务数据)
-- 第 5 节 yxe_visit_list 三列 —— 三列均已存在(visit_type/visit_date/submitter_name)
-- 第 6 节 yxe_user_task.income —— 列已存在,跳过
-- 第 7 节 sys_user.user_group —— 列已存在,跳过
-- 第 8 节 三张建表 —— 三表均已存在,跳过
-- 第 9 节 chain_id 占位符修正 —— 已执行:受影响 5 行;
-- 执行后 remaining_placeholder = 0,mismatch = 0
-- 功能验证:按连锁筛选命中数
-- chainId=老百姓 279 -> 282(找回 id 119、130、169)
-- chainId=百姓人家 174 -> 176(找回 id 138、139)
-- 第 10 节 yxe_task.reward 规范化 —— **尚未执行**:本地库仍有
-- yxe_task.id=663(title='测试手动',status=2)reward 为 NULL。
-- 接口已因读侧兜底不再报错,但金额按 0 计;如需数据一致请执行本节。
--
-- 生产库:尚未执行。需经确认后按本文件自上而下复核执行。
--
-- 备注(第 5 节):yxe_visit_list.visit_type 列的注释在本地库仍是旧值
-- 「拜访对象类型: 门店/门店经理/其他」,与源码中的新值「地点/...」不一致。
-- 因该列已存在,第 5 节按幂等设计跳过,不会修正注释;如需同步,见第 5 节备注。
-- ============================================================================