diff --git a/admin-console/src/views/SettingsView.vue b/admin-console/src/views/SettingsView.vue
index a33a10a..aafbf63 100644
--- a/admin-console/src/views/SettingsView.vue
+++ b/admin-console/src/views/SettingsView.vue
@@ -125,6 +125,12 @@
+
+
+ 智能诊断阈值(diagnosis_config)
+ 下发给小程序的实时诊断阈值,改此处即可调参无需发版。levels 为归一化吸收阈值;problem_wavelength 将问题类型映射到波长码 IR=1,R=2,UV=3,Y=4。保存前会校验 JSON 格式。
+
+
@@ -155,7 +161,8 @@ export default {
maintenance_mode: false
},
timezoneOptions: ['Asia/Shanghai', 'Asia/Tokyo', 'America/New_York', 'Europe/London'],
- passwordForm: { old_password: '', new_password: '', confirm_password: '' }
+ passwordForm: { old_password: '', new_password: '', confirm_password: '' },
+ diagnosisConfigText: ''
}
},
mounted() {
@@ -166,15 +173,25 @@ export default {
try {
const data = await get('/api/v1/admin/settings')
if (data) {
- Object.assign(this.settings, data)
+ const { diagnosis_config, ...rest } = data
+ Object.assign(this.settings, rest)
+ this.diagnosisConfigText = JSON.stringify(diagnosis_config || {}, null, 2)
}
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
}
},
async onSave() {
+ let diagnosisConfig
try {
- await post('/api/v1/admin/settings', this.settings)
+ diagnosisConfig = JSON.parse(this.diagnosisConfigText || '{}')
+ } catch (e) {
+ uni.showToast({ title: '诊断阈值 JSON 格式错误', icon: 'none' })
+ return
+ }
+ try {
+ const payload = { ...this.settings, diagnosis_config: diagnosisConfig }
+ await post('/api/v1/admin/settings', payload)
uni.showToast({ title: '保存成功', icon: 'success' })
} catch (e) {
uni.showToast({ title: '保存失败', icon: 'none' })
@@ -303,6 +320,26 @@ export default {
font-size: 16px;
}
+.config-desc {
+ font-size: 12px;
+ color: #999;
+ line-height: 1.6;
+ margin-bottom: 12px;
+}
+
+.config-editor {
+ width: 100%;
+ min-height: 200px;
+ border: 1px solid #d9d9d9;
+ border-radius: 6px;
+ padding: 12px;
+ font-size: 13px;
+ font-family: 'Menlo', 'Consolas', monospace;
+ color: #333;
+ box-sizing: border-box;
+ line-height: 1.5;
+}
+
.toggle-list {
display: flex;
flex-direction: column;
diff --git a/server/sql/schema.sql b/server/sql/schema.sql
index da42a8f..1ada10b 100644
--- a/server/sql/schema.sql
+++ b/server/sql/schema.sql
@@ -84,6 +84,24 @@ CREATE TABLE IF NOT EXISTS treatment_records (
CONSTRAINT fk_treatment_user FOREIGN KEY (user_id) REFERENCES users (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CREATE TABLE IF NOT EXISTS scan_reports (
+ id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
+ user_id BIGINT UNSIGNED NOT NULL,
+ device_id VARCHAR(32) NULL,
+ scanned_at DATETIME NULL,
+ regions JSON NULL COMMENT 'array of per-region analysis',
+ overall JSON NULL COMMENT 'array of overall tendencies',
+ recommend_mask INT NOT NULL DEFAULT 0 COMMENT 'bitmask of regions recommended for care',
+ recommend_plan JSON NULL COMMENT 'array of {wavelength, mask, label_key}',
+ raw_pd JSON NULL COMMENT 'raw PD samples for the calibration dataset (may be partial)',
+ calibrated TINYINT NOT NULL DEFAULT 0 COMMENT 'whether thresholds were calibrated',
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ KEY idx_scan_reports_user_created (user_id, created_at),
+ KEY idx_scan_reports_device_created (device_id, created_at),
+ CONSTRAINT fk_scan_reports_user FOREIGN KEY (user_id) REFERENCES users (user_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
CREATE TABLE IF NOT EXISTS device_events (
event_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
device_id VARCHAR(32) NOT NULL,
@@ -185,4 +203,5 @@ INSERT IGNORE INTO system_settings (setting_key, setting_value) VALUES
('enable_register', 'true'),
('enable_binding', 'true'),
('enable_free_mode', 'true'),
-('maintenance_mode', 'false');
+('maintenance_mode', 'false'),
+('diagnosis_config', '{"calibrated": false, "note": "占位阈值,待真机标定后调整", "levels": {"low": 0.15, "mid": 0.35, "high": 0.55}, "problem_wavelength": {"aging": 2, "acne": 3, "pigment": 4, "deep": 1}}');
diff --git a/server/src/dao/scan-report.dao.js b/server/src/dao/scan-report.dao.js
new file mode 100644
index 0000000..dafcdee
--- /dev/null
+++ b/server/src/dao/scan-report.dao.js
@@ -0,0 +1,79 @@
+const { query, one, limitClause } = require('../lib/db')
+
+/**
+ * Create a scan report
+ * @param {Object} report
+ * @param {number} report.user_id
+ * @param {string|null} report.device_id
+ * @param {string|null} report.scanned_at - MySQL datetime string
+ * @param {string} report.regions - JSON string (array)
+ * @param {string} report.overall - JSON string (array)
+ * @param {number} report.recommend_mask
+ * @param {string} report.recommend_plan - JSON string (array)
+ * @param {string} report.raw_pd - JSON string
+ * @param {number} report.calibrated - 0/1
+ * @returns {Promise