# encoding:utf-8
class TcmExam < ActiveRecord::Base
has_paper_trail
self.table_name = "tcm_exams"
belongs_to :wx_user
belongs_to :scale_user, :foreign_key => :scale_user_id
STATUS_ENUM = [["待处理", "pending"], ["处理中", "processing"], ["已完成", "completed"], ["失败", "failed"]]
def wx_user_contact
return "-" if wx_user.blank?
user = wx_user.user
return "-" if user.blank?
contact = user.tel
contact = user.email if contact.blank?
contact.blank? ? "-" : contact
end
def scale_user_name
return "-" if scale_user.blank?
scale_user.nick_name || "-"
end
# 解析 AI 报告内容
def parsed_ai_summary
return {} if ai_summary.blank?
begin
JSON.parse(ai_summary)
rescue
{}
end
end
# 解析观察图片
def parsed_observation_images
return {} if observation_images.blank?
begin
JSON.parse(observation_images)
rescue
{}
end
end
# 解析问诊答案
def parsed_question_answers
return {} if question_answers.blank?
begin
JSON.parse(question_answers)
rescue
{}
end
end
# 解析最新体脂秤结果
def parsed_latest_scale_result
return {} if latest_scale_result.blank?
begin
JSON.parse(latest_scale_result)
rescue
{}
end
end
# 状态标签
def status_label
case status
when "pending" then "待处理"
when "processing" then "处理中"
when "completed" then "已完成"
when "failed" then "失败"
else status || "-"
end
end
# 虚拟方法:调养与建议
def advice_section
parsed_ai_summary["advice"] || "-"
end
# 虚拟方法:望诊记录
def observation_section
parsed_ai_summary["observation"] || "-"
end
# 格式化内容,处理换行和 markdown 格式
def format_content(content)
return "-" if content.blank?
# 处理换行符
formatted = content.to_s
.gsub(/\\n/, "
")
.gsub(/\n/, "
")
.gsub(/\*\*([^*]+)\*\*/, '\1') # **粗体**
.gsub(/\*([^*]+)\*/, '\1') # *斜体*
formatted
end
# 格式化调养与建议的嵌套 JSON 结构
def format_advice(advice)
return "-" if advice.blank?
# 如果是字符串,尝试解析为 JSON
if advice.is_a?(String)
begin
advice = JSON.parse(advice)
rescue
return format_content(advice)
end
end
# 如果不是 Hash,直接格式化
return format_content(advice.to_s) unless advice.is_a?(Hash)
# 定义分类标签和图标
category_info = {
'饮食' => { icon: '🍵', color: '#059669' },
'运动' => { icon: '🏃', color: '#2563eb' },
'作息' => { icon: '🌙', color: '#7c3aed' },
'情志' => { icon: '💆', color: '#db2777' }
}
html = ''
advice.each do |category, items|
info = category_info[category] || { icon: '📌', color: '#6b7280' }
html += %Q{
}
html += %Q{
#{info[:icon]} #{category}
}
html += %Q{
}
if items.is_a?(Array)
items.each do |item|
html += %Q{- #{format_content(item)}
}
end
else
html += %Q{- #{format_content(items)}
}
end
html += '
'
end
html
end
rails_admin do
navigation_label '中医诊疗记录'
parent ScaleDevice
weight 3
list do
filters [:wx_user, :scale_user, :status, :created_at]
field :id do
label 'ID'
end
field :wx_user_id do
label '微信用户ID'
end
field :wx_user do
label '微信用户'
pretty_value do
bindings[:object].wx_user_contact
end
end
field :scale_user_id do
label '称用户ID'
end
field :scale_user do
label '称用户名'
pretty_value do
bindings[:object].scale_user_name
end
end
field :status do
label '状态'
pretty_value do
bindings[:object].status_label
end
end
field :ai_summary do
label '报告详情'
formatted_value do
exam = bindings[:object]
bindings[:view].link_to('查看报告', bindings[:view].rails_admin.show_path(model_name: 'tcm_exam', id: exam.id), class: 'btn btn-info btn-sm')
end
end
field :created_at do
label '创建时间'
end
end
show do
field :id
field :wx_user_id do
label '微信用户ID'
end
field :wx_user do
label '微信用户'
pretty_value do
bindings[:object].wx_user_contact
end
end
field :scale_user_id do
label '称用户ID'
end
field :scale_user do
label '称用户名'
pretty_value do
bindings[:object].scale_user_name
end
end
field :status do
label '状态'
pretty_value do
bindings[:object].status_label
end
end
# 望诊图片
field :observation_images do
label '望诊图片'
pretty_value do
exam = bindings[:object]
images = exam.parsed_observation_images
return "-" if images.empty?
image_labels = {
'tongue' => '舌面照',
'tongueBottom' => '舌底照',
'face' => '面部照',
'body' => '全身照'
}
html = ''
['tongue', 'tongueBottom', 'face', 'body'].each do |key|
urls = images[key]
next if urls.blank? || urls.empty?
url = urls.first
next if url.blank?
html += %Q{
#{image_labels[key] || key}
}
end
html += '
'
html.html_safe
end
end
# AI 报告内容 - 一般情况
field :ai_summary do
label '壹 · 一般情况'
pretty_value do
exam = bindings[:object]
summary = exam.parsed_ai_summary
content = summary['general'] || summary['summary'] || '-'
%Q{#{exam.format_content(content)}
}.html_safe
end
end
# 问诊
field :question_answers do
label '贰 · 问诊 (十问歌)'
pretty_value do
exam = bindings[:object]
summary = exam.parsed_ai_summary
content = summary['inquiry'] || '-'
%Q{#{exam.format_content(content)}
}.html_safe
end
end
# 望诊与闻诊 - 使用虚拟字段确保显示
field :observation_section do
label '叁 · 望诊与闻诊'
pretty_value do
exam = bindings[:object]
summary = exam.parsed_ai_summary
content = summary['observation'] || '-'
%Q{#{exam.format_content(content)}
}.html_safe
end
end
# 总体评估与体质辨识
field :latest_scale_result do
label '肆 · 总体评估与体质辨识'
pretty_value do
exam = bindings[:object]
summary = exam.parsed_ai_summary
content = summary['constitution'] || '-'
%Q{#{exam.format_content(content)}
}.html_safe
end
end
# 调养与建议 - 格式化嵌套 JSON
field :advice_section do
label '伍 · 调养与建议'
pretty_value do
exam = bindings[:object]
summary = exam.parsed_ai_summary
advice = summary['advice']
return "-" if advice.blank?
formatted = exam.format_advice(advice)
%Q{#{formatted}
}.html_safe
end
end
# 总结
field :model do
label '陆 · 总结'
pretty_value do
exam = bindings[:object]
summary = exam.parsed_ai_summary
content = summary['summary']
return "-" if content.blank?
%Q{#{exam.format_content(content)}
}.html_safe
end
end
field :created_at do
label '创建时间'
end
field :updated_at do
label '更新时间'
end
end
# 禁用编辑和删除,只允许查看
edit do
field :status, :enum do
enum do
STATUS_ENUM
end
end
end
end
end