# 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{
' 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