{"id":27,"date":"2026-07-28T13:42:41","date_gmt":"2026-07-28T05:42:41","guid":{"rendered":"http:\/\/www.dxkua.cn\/index.php\/2026\/07\/28\/%e7%ac%ac%e4%b8%89%e9%83%a8%e5%88%86%ef%bc%9a%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81%e8%a1%a5%e5%85%a8\/"},"modified":"2026-07-28T13:57:23","modified_gmt":"2026-07-28T05:57:23","slug":"%e7%ac%ac%e4%b8%89%e9%83%a8%e5%88%86%ef%bc%9a%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81%e8%a1%a5%e5%85%a8","status":"publish","type":"post","link":"https:\/\/www.dxkua.cn\/index.php\/2026\/07\/28\/%e7%ac%ac%e4%b8%89%e9%83%a8%e5%88%86%ef%bc%9a%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81%e8%a1%a5%e5%85%a8\/","title":{"rendered":"\u7b2c\u4e09\u90e8\u5206\uff1a\u5b8c\u6574\u4ee3\u7801\u8865\u5168"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n\n\n<p class=\"wp-block-paragraph\">\u672c\u90e8\u5206\u8865\u9f50 PROJECT_DOC.md \u548c DOC_APPENDIX.md \u4e2d\u7f3a\u5931\u7684\u6240\u6709\u5185\u5bb9\uff1a\n\u524d\u7aef\u5168\u90e8\u9875\u9762\u4ee3\u7801\u3001\u6240\u6709\u5de5\u5177\u7c7b\/\u914d\u7f6e\u7c7b\u3001\u6240\u6709 Mapper XML\u3001\u5b8c\u6574\u90e8\u7f72\u6d41\u7a0b\u3001Dubbo \u9a8c\u8bc1\u65b9\u6cd5\u3002<\/p>\n\n\n<\/blockquote>\n\n<hr \/>\n\n<h2 class=\"wp-block-heading\">26. \u524d\u7aef\u5168\u90e8\u9875\u9762\u4ee3\u7801<\/h2>\n\n\n<h3 class=\"wp-block-heading\">26.1 LoginView.vue\uff08\u7528\u6237\u767b\u5f55\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { ElMessage } from 'element-plus'\nimport userApi from '@\/api\/userApi.js'\nimport { userTokenStore } from '@\/stores\/token.js'\nimport { userToken } from '@\/stores\/user.js'\n\nconst router = useRouter()\nconst tokenStore = userTokenStore()\nconst userStore = userToken()\n\nconst loginForm = ref({ username: '', password: '', captchaInput: '' })\nconst captchaData = ref({ key: '', imageBase64Data: '' })\n\n\/\/ \u52a0\u8f7d\u9a8c\u8bc1\u7801\nfunction loadCaptcha() {\n  userApi.captcha().then(resp =&gt; {\n    if (resp.code === 200) captchaData.value = resp.data\n  })\n}\n\n\/\/ \u767b\u5f55\nfunction login() {\n  if (!loginForm.value.username || !loginForm.value.password) {\n    ElMessage.warning('\u8bf7\u8f93\u5165\u7528\u6237\u540d\u548c\u5bc6\u7801')\n    return\n  }\n  userApi.login({\n    username: loginForm.value.username,\n    password: loginForm.value.password,\n    key: captchaData.value.key,\n    captchaInput: loginForm.value.captchaInput\n  }).then(resp =&gt; {\n    if (resp.code === 200) {\n      tokenStore.updateToken(resp.data)  \/\/ \u5b58 JWT\n      ElMessage.success('\u767b\u5f55\u6210\u529f')\n      \/\/ \u83b7\u53d6\u7528\u6237\u6570\u636e\n      userApi.getInfo().then(r =&gt; {\n        if (r.code === 200) userStore.updateUser(r.data)\n      })\n      router.push('\/user\/index')\n    } else {\n      ElMessage.error(resp.msg)\n      loadCaptcha()\n    }\n  })\n}\n\nloadCaptcha()\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;div class=\"login-page\"&gt;\n    &lt;el-card class=\"login-card\" shadow=\"always\"&gt;\n      &lt;h2&gt;\u7528\u6237\u767b\u5f55&lt;\/h2&gt;\n      &lt;el-form :model=\"loginForm\" label-width=\"0\"&gt;\n        &lt;el-form-item&gt;\n          &lt;el-input v-model=\"loginForm.username\" placeholder=\"\u8bf7\u8f93\u5165\u7528\u6237\u540d\" prefix-icon=\"User\" \/&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-input v-model=\"loginForm.password\" type=\"password\" placeholder=\"\u8bf7\u8f93\u5165\u5bc6\u7801\"\n                    prefix-icon=\"Lock\" show-password \/&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;div class=\"captcha-row\"&gt;\n            &lt;el-input v-model=\"loginForm.captchaInput\" placeholder=\"\u9a8c\u8bc1\u7801\" style=\"width: 60%\" \/&gt;\n            &lt;img :src=\"captchaData.imageBase64Data\" @click=\"loadCaptcha\"\n                 class=\"captcha-img\" title=\"\u70b9\u51fb\u5237\u65b0\" \/&gt;\n          &lt;\/div&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-button type=\"primary\" style=\"width: 100%\" @click=\"login\"&gt;\u767b \u5f55&lt;\/el-button&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-link type=\"primary\" @click=\"router.push('\/user\/reg')\"&gt;\u8fd8\u6ca1\u6709\u8d26\u53f7\uff1f\u7acb\u5373\u6ce8\u518c&lt;\/el-link&gt;\n        &lt;\/el-form-item&gt;\n      &lt;\/el-form&gt;\n    &lt;\/el-card&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.login-page {\n  display: flex; justify-content: center; align-items: center;\n  min-height: 500px; background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%);\n}\n.login-card {\n  width: 420px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); padding: 40px;\n}\n.login-card h2 { text-align: center; margin-bottom: 30px; color: #333; }\n.captcha-row { display: flex; gap: 12px; align-items: center; }\n.captcha-img { height: 38px; cursor: pointer; border: 1px solid #ddd; border-radius: 4px; }\n&lt;\/style&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.2 RegView.vue\uff08\u7528\u6237\u6ce8\u518c\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { ElMessage } from 'element-plus'\nimport userApi from '@\/api\/userApi.js'\n\nconst router = useRouter()\nconst form = ref({ username: '', password: '', password1: '', captchaInput: '' })\nconst captchaData = ref({ key: '', imageBase64Data: '' })\n\nfunction loadCaptcha() {\n  userApi.captcha().then(resp =&gt; {\n    if (resp.code === 200) captchaData.value = resp.data\n  })\n}\n\nfunction reg() {\n  if (!form.value.username || !form.value.password) {\n    ElMessage.warning('\u7528\u6237\u540d\u548c\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a'); return\n  }\n  if (form.value.password !== form.value.password1) {\n    ElMessage.warning('\u4e24\u6b21\u5bc6\u7801\u4e0d\u4e00\u81f4'); return\n  }\n  userApi.reg({\n    username: form.value.username,\n    password: form.value.password,\n    key: captchaData.value.key,\n    captchaInput: form.value.captchaInput\n  }).then(resp =&gt; {\n    if (resp.code === 200) {\n      ElMessage.success('\u6ce8\u518c\u6210\u529f\uff0c\u8bf7\u767b\u5f55')\n      router.push('\/user\/login')\n    } else {\n      ElMessage.error(resp.msg)\n      loadCaptcha()\n    }\n  })\n}\n\nloadCaptcha()\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;div class=\"reg-page\"&gt;\n    &lt;el-card class=\"reg-card\" shadow=\"always\"&gt;\n      &lt;h2&gt;\u7528\u6237\u6ce8\u518c&lt;\/h2&gt;\n      &lt;el-form :model=\"form\" label-width=\"0\"&gt;\n        &lt;el-form-item&gt;\n          &lt;el-input v-model=\"form.username\" placeholder=\"\u8bf7\u8f93\u5165\u7528\u6237\u540d\" \/&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-input v-model=\"form.password\" type=\"password\" placeholder=\"\u8bf7\u8f93\u5165\u5bc6\u7801\" show-password \/&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-input v-model=\"form.password1\" type=\"password\" placeholder=\"\u786e\u8ba4\u5bc6\u7801\" show-password \/&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;div class=\"captcha-row\"&gt;\n            &lt;el-input v-model=\"form.captchaInput\" placeholder=\"\u9a8c\u8bc1\u7801\" style=\"width: 60%\" \/&gt;\n            &lt;img :src=\"captchaData.imageBase64Data\" @click=\"loadCaptcha\" class=\"captcha-img\" title=\"\u70b9\u51fb\u5237\u65b0\" \/&gt;\n          &lt;\/div&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-button type=\"primary\" style=\"width: 100%\" @click=\"reg\"&gt;\u6ce8 \u518c&lt;\/el-button&gt;\n        &lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;\n          &lt;el-link type=\"primary\" @click=\"router.push('\/user\/login')\"&gt;\u5df2\u6709\u8d26\u53f7\uff1f\u7acb\u5373\u767b\u5f55&lt;\/el-link&gt;\n        &lt;\/el-form-item&gt;\n      &lt;\/el-form&gt;\n    &lt;\/el-card&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.reg-page { display: flex; justify-content: center; align-items: center; min-height: 500px; background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%); }\n.reg-card { width: 420px; border-radius: 12px; padding: 40px; }\n.reg-card h2 { text-align: center; margin-bottom: 30px; color: #333; }\n.captcha-row { display: flex; gap: 12px; align-items: center; }\n.captcha-img { height: 38px; cursor: pointer; border: 1px solid #ddd; border-radius: 4px; }\n&lt;\/style&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.3 SearchView.vue\uff08\u5546\u54c1\u641c\u7d22\/\u5206\u7c7b\u6d4f\u89c8\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref, onMounted, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport goodsApi from '@\/api\/GoodsApi.js'\nimport categoryApi from '@\/api\/CategoryApi.js'\n\nconst route = useRoute()\nconst router = useRouter()\nconst SERVER_ADDR = ref(import.meta.env.VITE_SERVER_ADDR)\n\nconst goodsList = ref([])\nconst total = ref(0)\nconst pageNum = ref(1)\nconst pageSize = ref(20)\nconst categories = ref([])\nconst activeCategoryId = ref(null)\n\nfunction loadCategories() {\n  categoryApi.selectAllParent().then(resp =&gt; {\n    if (resp.code === 200) categories.value = resp.data || []\n  })\n}\n\nfunction loadGoods() {\n  const cid = route.params.categoryId || activeCategoryId.value\n  goodsApi.selectByPage1({ categoryId: cid || undefined }, pageNum.value, pageSize.value)\n    .then(resp =&gt; {\n      if (resp.data) {\n        goodsList.value = resp.data.list || []\n        total.value = resp.data.total || 0\n      }\n    })\n}\n\nfunction onPageChange(p) { pageNum.value = p; loadGoods() }\nfunction selectCategory(cid) {\n  activeCategoryId.value = cid\n  pageNum.value = 1\n  loadGoods()\n}\n\nwatch(() =&gt; route.params.categoryId, () =&gt; {\n  activeCategoryId.value = Number(route.params.categoryId) || null\n  pageNum.value = 1\n  loadGoods()\n})\n\nonMounted(() =&gt; { loadCategories(); loadGoods() })\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;div class=\"search-page\"&gt;\n    &lt;!-- \u5206\u7c7b\u6807\u7b7e --&gt;\n    &lt;div class=\"category-bar\"&gt;\n      &lt;span :class=\"{ tag: true, active: !activeCategoryId }\" @click=\"selectCategory(null)\"&gt;\u5168\u90e8&lt;\/span&gt;\n      &lt;span v-for=\"c in categories\" :key=\"c.id\"\n            :class=\"{ tag: true, active: activeCategoryId === c.id }\"\n            @click=\"selectCategory(c.id)\"&gt;{{ c.name }}&lt;\/span&gt;\n    &lt;\/div&gt;\n\n    &lt;!-- \u5546\u54c1\u7f51\u683c --&gt;\n    &lt;div v-if=\"goodsList.length &gt; 0\" class=\"goods-grid\"&gt;\n      &lt;div v-for=\"g in goodsList\" :key=\"g.id\" class=\"goods-card\"\n           @click=\"router.push({ path: '\/user\/goods', query: { id: g.id } })\"&gt;\n        &lt;el-image v-if=\"g.picList &amp;&amp; g.picList.length\"\n                  :src=\"SERVER_ADDR + '\/goods\/pic\/' + g.picList[0].url\"\n                  fit=\"cover\" style=\"width:224px;height:224px\" \/&gt;\n        &lt;div v-else class=\"no-pic\"&gt;\u6682\u65e0\u56fe\u7247&lt;\/div&gt;\n        &lt;div class=\"card-info\"&gt;\n          &lt;p class=\"gd-name\"&gt;{{ g.name }}&lt;\/p&gt;\n          &lt;p class=\"gd-price\"&gt;\uffe5{{ g.price }}&lt;\/p&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n    &lt;el-empty v-else description=\"\u6ca1\u6709\u627e\u5230\u5546\u54c1\" \/&gt;\n\n    &lt;el-pagination v-if=\"total &gt; pageSize\" style=\"margin-top:20px;text-align:center\"\n                   background layout=\"prev, pager, next\"\n                   :total=\"total\" :page-size=\"pageSize\"\n                   v-model:current-page=\"pageNum\" @current-change=\"onPageChange\" \/&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.search-page { margin: 20px 15px; }\n.category-bar { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; }\n.tag {\n  padding: 6px 18px; border-radius: 20px; background: #f5f5f5; cursor: pointer;\n  font-size: 14px; transition: all 0.2s;\n}\n.tag:hover { background: #ffe6d0; }\n.tag.active { background: var(--theme-color, #FF6800); color: #fff; }\n.goods-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }\n.goods-card {\n  background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n  overflow: hidden; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;\n}\n.goods-card:hover { transform: translateY(-4px); box-shadow: 0 4px 16px rgba(0,0,0,0.12); }\n.no-pic { width:224px; height:224px; background:#f5f5f5; display:flex; align-items:center; justify-content:center; color:#999; }\n.card-info { padding: 10px 12px; }\n.gd-name { font-size:14px; color:#333; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-bottom:6px; }\n.gd-price { font-size:16px; color:var(--theme-color,#FF6800); font-weight:bold; }\n&lt;\/style&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.4 IndexView.vue \u2014 \u7528\u6237\u9996\u9875<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref, onMounted } from 'vue'\nimport { useRouter } from 'vue-router'\nimport goodsApi from '@\/api\/GoodsApi.js'\nimport categoryApi from '@\/api\/CategoryApi.js'\n\nconst router = useRouter()\nconst SERVER_ADDR = ref(import.meta.env.VITE_SERVER_ADDR)\nconst recomGroups = ref([])  \/\/ [{category, goodsList}, ...]\n\n\/\/ \u6309\u7236\u5206\u7c7b\u5206\u7ec4\u52a0\u8f7d\u63a8\u8350\u5546\u54c1\nfunction loadRecom() {\n  categoryApi.selectAllParent().then(resp =&gt; {\n    const parents = resp.data || []\n    const promises = parents.map(parent =&gt;\n      goodsApi.selectByPage1({ categoryId: parent.id, recom: 1 }, 1, 5)\n        .then(r =&gt; ({ category: parent, goodsList: r.data ? r.data.list : [] }))\n    )\n    Promise.all(promises).then(results =&gt; {\n      recomGroups.value = results.filter(g =&gt; g.goodsList.length &gt; 0)\n    })\n  })\n}\n\nonMounted(loadRecom)\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;div class=\"index-page\"&gt;\n    &lt;!-- \u8f6e\u64ad\u56fe --&gt;\n    &lt;el-carousel height=\"650px\" motion-blur&gt;\n      &lt;el-carousel-item v-for=\"i in 3\" :key=\"i\"&gt;\n        &lt;div :style=\"{ background: ['#303133','#FF6800','#409EFF'][i-1],\n                       height:'100%', display:'flex', alignItems:'center', justifyContent:'center',\n                       color:'#fff', fontSize:'48px', fontWeight:'bold' }\"&gt;\n          {{ ['\u65b0\u54c1\u9996\u53d1','\u9650\u65f6\u7279\u60e0','\u54c1\u724c\u76f4\u4f9b'][i-1] }}\n        &lt;\/div&gt;\n      &lt;\/el-carousel-item&gt;\n    &lt;\/el-carousel&gt;\n\n    &lt;!-- \u5e7f\u544a\u4f4d --&gt;\n    &lt;el-row :gutter=\"20\" class=\"ad-row\"&gt;\n      &lt;el-col :span=\"6\" v-for=\"(ad,i) in ['\u624b\u673a\u6570\u7801','\u7535\u8111\u529e\u516c','\u5bb6\u7528\u7535\u5668','\u667a\u80fd\u5bb6\u5c45']\" :key=\"i\"&gt;\n        &lt;div class=\"ad-card\" @click=\"router.push('\/user\/search')\"&gt;\n          &lt;div class=\"ad-icon\"&gt;{{ ['\ud83d\udcf1','\ud83d\udcbb','\ud83d\udcfa','\ud83c\udfe0'][i] }}&lt;\/div&gt;\n          &lt;p&gt;{{ ad }}&lt;\/p&gt;\n        &lt;\/div&gt;\n      &lt;\/el-col&gt;\n    &lt;\/el-row&gt;\n\n    &lt;!-- \u6309\u7236\u5206\u7c7b\u5206\u7ec4\u63a8\u8350\u5546\u54c1 --&gt;\n    &lt;div v-for=\"group in recomGroups\" :key=\"group.category.id\" class=\"recom-section\"&gt;\n      &lt;div class=\"section-title\"&gt;\n        &lt;span class=\"title-bar\"&gt;&lt;\/span&gt;\n        &lt;span class=\"title-text\" @click=\"router.push('\/user\/search\/'+group.category.id)\"&gt;\n          {{ group.category.name }} &lt;small&gt;\u67e5\u770b\u66f4\u591a &amp;gt;&lt;\/small&gt;\n        &lt;\/span&gt;\n      &lt;\/div&gt;\n      &lt;div class=\"goods-row\"&gt;\n        &lt;div v-for=\"g in group.goodsList\" :key=\"g.id\" class=\"goods-card\"\n             @click=\"router.push({path:'\/user\/goods',query:{id:g.id}})\"&gt;\n          &lt;el-image v-if=\"g.picList &amp;&amp; g.picList.length\"\n                    :src=\"SERVER_ADDR + '\/goods\/pic\/' + g.picList[0].url\"\n                    fit=\"cover\" style=\"width:224px;height:224px\" \/&gt;\n          &lt;div v-else class=\"no-pic\"&gt;\u6682\u65e0\u56fe\u7247&lt;\/div&gt;\n          &lt;div class=\"card-info\"&gt;\n            &lt;p class=\"gd-name\"&gt;{{ g.name }}&lt;\/p&gt;\n            &lt;p class=\"gd-price\"&gt;\uffe5{{ g.price }}&lt;\/p&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.index-page { margin: 0 15px; }\n.ad-row { margin: 20px 0; }\n.ad-card { text-align: center; padding: 20px; background: #fff; border-radius: 8px; cursor: pointer; transition: all 0.2s; }\n.ad-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); transform: translateY(-2px); }\n.ad-icon { font-size: 40px; margin-bottom: 8px; }\n.ad-card p { color: #333; font-size: 14px; margin: 0; }\n.recom-section { margin: 30px 0; }\n.section-title { display: flex; align-items: center; margin-bottom: 16px; }\n.title-bar { width: 4px; height: 20px; background: var(--theme-color, #FF6800); margin-right: 10px; border-radius: 2px; }\n.title-text { font-size: 18px; font-weight: bold; color: #333; cursor: pointer; }\n.title-text small { font-size: 13px; color: #999; font-weight: normal; }\n.goods-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }\n.goods-card { background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); overflow: hidden; cursor: pointer; transition: all 0.2s; }\n.goods-card:hover { transform: translateY(-4px); box-shadow: 0 4px 16px rgba(0,0,0,0.12); }\n.no-pic { width:224px; height:224px; background:#f5f5f5; display:flex; align-items:center; justify-content:center; color:#999; }\n.card-info { padding: 10px 12px; }\n.gd-name { font-size:14px; color:#333; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-bottom:6px; }\n.gd-price { font-size:16px; color:var(--theme-color,#FF6800); font-weight:bold; }\n&lt;\/style&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.5 HomeView.vue\uff08\u7528\u6237\u7aef\u7236\u5e03\u5c40\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport UserHeader from '@\/components\/user\/home\/UserHeader.vue'\nimport UserFooter from '@\/components\/user\/home\/UserFooter.vue'\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;el-container&gt;\n    &lt;el-header style=\"padding:0;height:auto\"&gt;\n      &lt;UserHeader \/&gt;\n    &lt;\/el-header&gt;\n    &lt;el-main style=\"background:#eee;min-height:500px;padding:0\"&gt;\n      &lt;div class=\"center\"&gt;\n        &lt;RouterView \/&gt;\n      &lt;\/div&gt;\n    &lt;\/el-main&gt;\n    &lt;el-footer style=\"padding:0;height:auto\"&gt;\n      &lt;UserFooter \/&gt;\n    &lt;\/el-footer&gt;\n  &lt;\/el-container&gt;\n&lt;\/template&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.6 UserHeader.vue\uff08\u7528\u6237\u5bfc\u822a\u680f\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { userToken } from '@\/stores\/user.js'\nimport { userTokenStore } from '@\/stores\/token.js'\n\nconst router = useRouter()\nconst userStore = userToken()\nconst tokenStore = userTokenStore()\nconst keyword = ref('')\n\nfunction search() {\n  if (keyword.value.trim()) {\n    router.push({ path: '\/user\/search', query: { keyword: keyword.value.trim() } })\n  }\n}\n\nfunction logout() {\n  tokenStore.$reset()\n  userStore.$reset()\n  router.push('\/user\/index')\n}\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;!-- \u9876\u90e8\u5bfc\u822a\u6761 --&gt;\n  &lt;div class=\"nav_bg\"&gt;\n    &lt;div class=\"center top-nav\"&gt;\n      &lt;div&gt;\n        &lt;router-link to=\"\/user\/index\"&gt;\u9996\u9875&lt;\/router-link&gt;\n        &lt;router-link to=\"\/user\/search\"&gt;\u5168\u90e8\u5546\u54c1&lt;\/router-link&gt;\n        &lt;a href=\"#\"&gt;\u65b0\u54c1\u4e0a\u5e02&lt;\/a&gt;\n        &lt;a href=\"#\"&gt;\u9650\u65f6\u7279\u60e0&lt;\/a&gt;\n        &lt;a href=\"#\"&gt;\u54c1\u724c\u4e13\u533a&lt;\/a&gt;\n        &lt;a href=\"#\"&gt;\u5173\u4e8e\u6211\u4eec&lt;\/a&gt;\n      &lt;\/div&gt;\n      &lt;div&gt;\n        &lt;template v-if=\"tokenStore.tokenStr &amp;&amp; userStore.userInfo\"&gt;\n          &lt;span&gt;{{ userStore.userInfo.username }}&lt;\/span&gt;\n          &lt;a @click=\"logout\"&gt;\u9000\u51fa&lt;\/a&gt;\n          &lt;router-link to=\"\/user\/cart\"&gt;\u8d2d\u7269\u8f66&lt;\/router-link&gt;\n        &lt;\/template&gt;\n        &lt;template v-else&gt;\n          &lt;router-link to=\"\/user\/login\"&gt;\u767b\u5f55&lt;\/router-link&gt;\n          &lt;router-link to=\"\/user\/reg\"&gt;\u6ce8\u518c&lt;\/router-link&gt;\n        &lt;\/template&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n\n  &lt;!-- Logo+\u641c\u7d22\u533a --&gt;\n  &lt;div class=\"center search-area\"&gt;\n    &lt;div class=\"logo\"&gt;\u5728\u7ebf\u5546\u57ce&lt;\/div&gt;\n    &lt;div class=\"search-box\"&gt;\n      &lt;el-input v-model=\"keyword\" placeholder=\"\u641c\u7d22\u5546\u54c1\" @keyup.enter=\"search\" size=\"large\" \/&gt;\n      &lt;el-button type=\"primary\" @click=\"search\" size=\"large\"&gt;\u641c\u7d22&lt;\/el-button&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n\n  &lt;!-- \u5206\u7c7b\u83dc\u5355 --&gt;\n  &lt;div class=\"cat-bar\"&gt;\n    &lt;div class=\"center cat-list\"&gt;\n      &lt;span v-for=\"c in ['\u667a\u80fd\u624b\u673a','\u7535\u8111\u529e\u516c','\u5bb6\u7528\u7535\u5668','\u667a\u80fd\u5bb6\u5c45','\u8fd0\u52a8\u6237\u5916']\" :key=\"c\"\n            @click=\"router.push('\/user\/search')\"&gt;{{ c }}&lt;\/span&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.nav_bg { background: var(--theme-bg-color, #303133); height: 50px; line-height: 50px; }\n.top-nav { display: flex; justify-content: space-between; color: #ccc; font-size: 14px; }\n.top-nav a, .top-nav span { color: #ccc; margin: 0 12px; cursor: pointer; text-decoration: none; }\n.top-nav a:hover { color: var(--theme-color, #FF6800); }\n.search-area { display: flex; align-items: center; height: 80px; }\n.logo { font-size: 28px; font-weight: bold; color: var(--theme-color, #FF6800); margin-right: 40px; }\n.search-box { display: flex; width: 500px; }\n.cat-bar { background: var(--theme-color, #FF6800); height: 40px; line-height: 40px; }\n.cat-list { display: flex; gap: 30px; color: #fff; font-size: 14px; cursor: pointer; }\n.cat-list span { cursor: pointer; }\n.cat-list span:hover { color: wheat; }\n&lt;\/style&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.7 CartView.vue\uff08\u8d2d\u7269\u8f66 \u2014 \u5b8c\u6574\u7248\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref } from 'vue'\nimport cartApi from '@\/api\/cartApi.js'\nimport { Delete, ShoppingCart } from '@element-plus\/icons-vue'\nimport { ElMessage } from 'element-plus'\nimport { useRouter } from 'vue-router'\n\nconst router = useRouter()\nconst SERVER_ADDR = ref(import.meta.env.VITE_SERVER_ADDR)\nconst cartList = ref([])\nconst checkAllState = ref(false)\nconst halfState = ref(false)\nconst checkCount = ref(0)\nconst checkPrice = ref(0)\n\nfunction selectBycondition() {\n  cartApi.selectBycodition().then(resp =&gt; {\n    cartList.value = resp.data || []\n    changeState()\n  })\n}\n\nfunction checkAllOrderNone() {\n  cartList.value.forEach(c =&gt; c.checkState = checkAllState.value)\n  changeState()\n}\n\nfunction changeState() {\n  let count = 0\n  cartList.value.forEach(c =&gt; { if (c.checkState) count++ })\n  checkAllState.value = count === cartList.value.length &amp;&amp; count &gt; 0\n  halfState.value = count &gt; 0 &amp;&amp; count &lt; cartList.value.length\n  checkedPriceAndCount()\n}\n\nfunction checkedPriceAndCount() {\n  checkCount.value = 0; checkPrice.value = 0\n  cartList.value.forEach(c =&gt; {\n    if (c.checkState) {\n      checkCount.value += c.count\n      if (c.goods) checkPrice.value += c.goods.price * c.count\n    }\n  })\n}\n\nfunction updatecount(cart) {\n  cartApi.update({ id: cart.id, count: cart.count }).then(resp =&gt; {\n    if (resp.code === 200) {\n      cartApi.selectById(cart.id).then(r =&gt; {\n        cartList.value.forEach(c =&gt; { if (c.id === cart.id) c.count = r.data.count })\n        checkedPriceAndCount()\n      })\n    } else ElMessage.error(resp.msg)\n  })\n}\n\nfunction deleteCart(id) {\n  cartApi.delete(id).then(resp =&gt; {\n    if (resp.code === 200) {\n      ElMessage.success(resp.msg)\n      cartList.value = cartList.value.filter(c =&gt; c.id !== id)\n      changeState()\n    }\n  })\n}\n\nfunction deleteChecked() {\n  const ids = cartList.value.filter(c =&gt; c.checkState).map(c =&gt; c.id)\n  if (ids.length === 0) { return ElMessage.warning('\u6ca1\u6709\u9009\u62e9\u5546\u54c1') }\n  cartApi.deleteByIds(ids).then(resp =&gt; {\n    if (resp.code === 200) { ElMessage.success(resp.msg); selectBycondition() }\n  })\n}\n\nfunction deleteAll() {\n  const ids = cartList.value.map(c =&gt; c.id)\n  if (ids.length === 0) return\n  cartApi.deleteByIds(ids).then(resp =&gt; {\n    if (resp.code === 200) { ElMessage.success(resp.msg); selectBycondition() }\n  })\n}\n\nfunction toCreateOrder() {\n  const ids = cartList.value.filter(c =&gt; c.checkState).map(c =&gt; c.id)\n  if (ids.length === 0) { ElMessage.warning('\u8bf7\u9009\u62e9\u8981\u7ed3\u7b97\u7684\u5546\u54c1'); return }\n  router.push({ path: '\/user\/createOrder', query: { cartIds: ids } })\n}\n\nselectBycondition()\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;div v-if=\"cartList.length &gt; 0\"&gt;\n    &lt;el-row class=\"title\"&gt;&lt;el-col :span=\"2\"&gt;&lt;el-checkbox v-model=\"checkAllState\" :indeterminate=\"halfState\" @change=\"checkAllOrderNone\"&gt;\u5168\u9009&lt;\/el-checkbox&gt;&lt;\/el-col&gt;&lt;el-col :span=\"11\"&gt;\u5546\u54c1&lt;\/el-col&gt;&lt;el-col :span=\"2\"&gt;\u5355\u4ef7&lt;\/el-col&gt;&lt;el-col :span=\"6\"&gt;\u6570\u91cf&lt;\/el-col&gt;&lt;el-col :span=\"2\"&gt;\u5c0f\u8ba1&lt;\/el-col&gt;&lt;el-col :span=\"1\"&gt;\u64cd\u4f5c&lt;\/el-col&gt;&lt;\/el-row&gt;\n    &lt;el-row v-for=\"cart in cartList\" :key=\"cart.id\" class=\"cartItem\"&gt;\n      &lt;el-col :span=\"2\"&gt;&lt;el-checkbox v-model=\"cart.checkState\" @change=\"changeState\"\/&gt;&lt;\/el-col&gt;\n      &lt;el-col :span=\"2\"&gt;&lt;el-image v-if=\"cart.goods?.picList?.length\" :src=\"SERVER_ADDR+'\/goods\/pic\/'+cart.goods.picList[0].url\" style=\"width:50px;height:50px\" fit=\"contain\"\/&gt;&lt;div v-else class=\"no-pic-sm\"&gt;\u65e0\u56fe&lt;\/div&gt;&lt;\/el-col&gt;\n      &lt;el-col :span=\"9\"&gt;{{ cart.goods?.name || '\u5546\u54c1\u5df2\u4e0b\u67b6' }}&lt;\/el-col&gt;\n      &lt;el-col :span=\"2\"&gt;{{ cart.goods?.price || '-' }}&lt;\/el-col&gt;\n      &lt;el-col :span=\"6\"&gt;&lt;el-input-number v-model=\"cart.count\" :min=\"1\" @change=\"updatecount(cart)\"\/&gt;&lt;\/el-col&gt;\n      &lt;el-col :span=\"2\"&gt;{{ cart.goods ? (cart.goods.price * cart.count).toFixed(2) : '-' }}&lt;\/el-col&gt;\n      &lt;el-col :span=\"1\"&gt;&lt;el-button type=\"danger\" @click=\"deleteCart(cart.id)\"&gt;&lt;el-icon&gt;&lt;delete\/&gt;&lt;\/el-icon&gt;&lt;\/el-button&gt;&lt;\/el-col&gt;\n    &lt;\/el-row&gt;\n    &lt;el-row class=\"title\"&gt;&lt;el-col :span=\"2\"&gt;&lt;el-checkbox v-model=\"checkAllState\" :indeterminate=\"halfState\" @change=\"checkAllOrderNone\"&gt;\u5168\u9009&lt;\/el-checkbox&gt;&lt;\/el-col&gt;&lt;el-col :span=\"3\"&gt;&lt;el-link :underline=\"false\" @click=\"deleteChecked\"&gt;\u5220\u9664\u9009\u4e2d&lt;\/el-link&gt;&lt;\/el-col&gt;&lt;el-col :span=\"11\"&gt;&lt;el-link :underline=\"false\" @click=\"deleteAll\"&gt;\u6e05\u7406\u8d2d\u7269\u8f66&lt;\/el-link&gt;&lt;\/el-col&gt;&lt;el-col :span=\"3\"&gt;\u5df2\u9009{{checkCount}}\u4ef6&lt;\/el-col&gt;&lt;el-col :span=\"3\"&gt;\u603b\u4ef7: \u00a5{{checkPrice}}&lt;\/el-col&gt;&lt;el-col :span=\"2\"&gt;&lt;el-button type=\"danger\" @click=\"toCreateOrder\"&gt;\u7ed3\u7b97&lt;\/el-button&gt;&lt;\/el-col&gt;&lt;\/el-row&gt;\n  &lt;\/div&gt;\n  &lt;div v-else class=\"none\"&gt;\n    &lt;div class=\"empty-cart\"&gt;&lt;el-icon class=\"empty-icon\"&gt;&lt;ShoppingCart\/&gt;&lt;\/el-icon&gt;&lt;p class=\"empty-text\"&gt;\u8d2d\u7269\u8f66\u662f\u7a7a\u7684&lt;\/p&gt;&lt;p class=\"empty-hint\"&gt;\u5feb\u53bb\u6311\u9009\u5fc3\u4eea\u7684\u5546\u54c1\u5427&lt;\/p&gt;&lt;el-button type=\"primary\" @click=\"router.push('\/user\/index')\"&gt;\u53bb\u901b\u901b&lt;\/el-button&gt;&lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.title { margin:20px 0; padding-left:10px; font-size:14px; background:#fff; height:40px; line-height:40px; }\n.cartItem { border-bottom:1px solid #eee; padding-left:10px; font-size:14px; background:#fff; height:60px; line-height:60px; transition:background 0.2s; }\n.cartItem:hover { background:#fafafa; }\n.no-pic-sm { width:50px; height:50px; background:#f5f5f5; display:flex; align-items:center; justify-content:center; color:#999; font-size:12px; }\n.none { display:flex; align-items:center; justify-content:center; min-height:400px; background:#fff; }\n.empty-cart { text-align:center; }\n.empty-icon { font-size:64px; color:#ccc; margin-bottom:16px; }\n.empty-text { font-size:18px; color:#666; margin-bottom:8px; }\n.empty-hint { font-size:14px; color:#999; margin-bottom:20px; }\n&lt;\/style&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">26.8 admin LoginView.vue\uff08\u7ba1\u7406\u5458\u767b\u5f55\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>&lt;script setup&gt;\nimport { ref } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { ElMessage } from 'element-plus'\nimport adminApi from '@\/api\/AdminApi.js'\nimport { userTokenStore } from '@\/stores\/token.js'\n\nconst router = useRouter()\nconst tokenStore = userTokenStore()\nconst form = ref({ username: '', password: '', captchaInput: '' })\nconst captchaData = ref({ key: '', imageBase64Data: '' })\n\nfunction loadCaptcha() {\n  adminApi.captcha().then(resp =&gt; { if (resp.code === 200) captchaData.value = resp.data })\n}\n\nfunction login() {\n  adminApi.login({ username: form.value.username, password: form.value.password, key: captchaData.value.key, captchaInput: form.value.captchaInput }).then(resp =&gt; {\n    if (resp.code === 200) { tokenStore.updateToken(resp.data); router.push('\/admin\/index') }\n    else { ElMessage.error(resp.msg); loadCaptcha() }\n  })\n}\n\nloadCaptcha()\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;div class=\"login-page\"&gt;\n    &lt;el-card class=\"login-card\"&gt;\n      &lt;h2&gt;\u7ba1\u7406\u5458\u767b\u5f55&lt;\/h2&gt;\n      &lt;el-form :model=\"form\" label-width=\"0\"&gt;\n        &lt;el-form-item&gt;&lt;el-input v-model=\"form.username\" placeholder=\"\u7528\u6237\u540d\" \/&gt;&lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;&lt;el-input v-model=\"form.password\" type=\"password\" placeholder=\"\u5bc6\u7801\" show-password \/&gt;&lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;&lt;div class=\"captcha-row\"&gt;&lt;el-input v-model=\"form.captchaInput\" placeholder=\"\u9a8c\u8bc1\u7801\" style=\"width:60%\"\/&gt;&lt;img :src=\"captchaData.imageBase64Data\" @click=\"loadCaptcha\" class=\"captcha-img\" title=\"\u5237\u65b0\"\/&gt;&lt;\/div&gt;&lt;\/el-form-item&gt;\n        &lt;el-form-item&gt;&lt;el-button type=\"primary\" style=\"width:100%\" @click=\"login\"&gt;\u767b \u5f55&lt;\/el-button&gt;&lt;\/el-form-item&gt;\n      &lt;\/el-form&gt;\n    &lt;\/el-card&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;style scoped&gt;\n.login-page { display:flex; justify-content:center; align-items:center; min-height:100vh; background:#f5f7fa; }\n.login-card { width:400px; border-radius:12px; padding:40px; }\n.login-card h2 { text-align:center; margin-bottom:30px; }\n.captcha-row { display:flex; gap:12px; align-items:center; }\n.captcha-img { height:38px; cursor:pointer; border:1px solid #ddd; border-radius:4px; }\n&lt;\/style&gt;<\/code><\/pre>\n\n<hr \/>\n\n<h2 class=\"wp-block-heading\">27. \u5168\u90e8\u5de5\u5177\u7c7b\u548c\u914d\u7f6e\u7c7b\u4ee3\u7801<\/h2>\n\n\n<h3 class=\"wp-block-heading\">27.1 RedisConfig<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>\/\/ common\/src\/main\/java\/org\/example\/mall\/common\/config\/RedisConfig.java\npackage org.example.mall.common.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\n\n@Configuration\npublic class RedisConfig {\n\n    @Bean\n    public RedisTemplate&lt;String, Object&gt; redisTemplate(RedisConnectionFactory factory) {\n        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();\n        template.setConnectionFactory(factory);\n        \/\/ Key \u5e8f\u5217\u5316\u7528 String\n        template.setKeySerializer(new StringRedisSerializer());\n        template.setHashKeySerializer(new StringRedisSerializer());\n        \/\/ Value \u5e8f\u5217\u5316\u7528 JSON\n        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());\n        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());\n        return template;\n    }\n}<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">27.2 \u5404\u5fae\u670d\u52a1 WebConfig \u5b8c\u6574\u4ee3\u7801<\/h3>\n\n\n<p class=\"wp-block-paragraph\"><strong>goods_service\/WebConfig.java<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>package org.example.mall.goods_service.config;\n\nimport org.example.mall.common.intercepter.JwtInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    @Value(\"${com.picdir}\")\n    private String picdir;\n\n    @Autowired\n    private JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n        \/\/ \u56fe\u7247\u9759\u6001\u6587\u4ef6\u6620\u5c04\uff1a\/category\/pic\/xxx.jpg \u2192 \u6587\u4ef6\u7cfb\u7edf picdir\/xxx.jpg\n        registry.addResourceHandler(\"\/category\/pic\/**\", \"\/goods\/pic\/**\")\n                .addResourceLocations(\"file:\" + picdir);\n    }\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        \/\/ \u53ea\u62e6\u622a\u5199\u64cd\u4f5c\uff08\u589e\u5220\u6539\uff09\uff0c\u8bfb\u64cd\u4f5c\u653e\u884c\n        registry.addInterceptor(jwtInterceptor)\n                .addPathPatterns(\"\/category\/**\", \"\/goods\/**\")\n                .excludePathPatterns(\n                    \"\/category\/search\", \"\/category\/allParent\",\n                    \"\/category\/{id}\", \"\/category\/**\/pic\/**\",\n                    \"\/goods\", \"\/goods\/search\", \"\/goods\/{id}\",\n                    \"\/goods\/**\/pic\/**\", \"\/goods\/upload\"\n                );\n    }\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>user_service\/WebConfig.java<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>package org.example.mall.user_service.config;\n\nimport org.example.mall.common.intercepter.JwtInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    @Autowired\n    private JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        \/\/ \/user\/** \u548c \/collect\/** \u9700\u8981\u8ba4\u8bc1\n        radio.addInterceptor(jwtInterceptor)\n                .addPathPatterns(\"\/user\/**\", \"\/collect\/**\");\n        \/\/ \/addr\/** \u4e0d\u5728\u62e6\u622a\u8303\u56f4\u2014\u2014AddrController\u6bcf\u4e2a\u65b9\u6cd5\u624b\u52a8\u6821\u9a8ctoken\n    }\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>admin_service\/WebConfig.java<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>package org.example.mall.admin_service.config;\n\nimport org.example.mall.common.intercepter.JwtInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    @Autowired\n    private JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(jwtInterceptor)\n                .addPathPatterns(\"\/admin\/**\")\n                .excludePathPatterns(\"\/admin\/captcha\", \"\/admin\/login\");\n    }\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>order_service\/WebConfig.java<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>package org.example.mall.order_service.config;\n\nimport org.example.mall.common.intercepter.JwtInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    @Value(\"${com.picdir}\")\n    private String picdir;\n\n    @Autowired\n    private JwtInterceptor jwtInterceptor;\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n        registry.addResourceHandler(\"\/category\/pic\/**\", \"\/goods\/pic\/**\")\n                .addResourceLocations(\"file:\" + picdir);\n    }\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        \/\/ \u8d2d\u7269\u8f66\u63a5\u53e3\u9700\u8981\u8ba4\u8bc1\n        registry.addInterceptor(jwtInterceptor)\n                .addPathPatterns(\"\/cart\/**\");\n        \/\/ \/order\/** \u4e0d\u62e6\u622a\u2014\u2014Controller\u6309\u7aef\u70b9\u533a\u5206\uff1a\n        \/\/ create\/pay\/cancel\/myOrders \u2192 \u624b\u52a8 @RequestHeader(\"token\")\n        \/\/ notify \u2192 \u4e0d\u9700\u8981token\uff08\u652f\u4ed8\u5b9d\u56de\u8c03\uff09\n        \/\/ query\/search\/selectById \u2192 \u5f00\u653e\u6216\u7cbe\u7b80\u8fd4\u56de\n    }\n}<\/code><\/pre>\n\n<hr \/>\n\n<h2 class=\"wp-block-heading\">28. \u5168\u90e8 Mapper XML \u5b8c\u6574 SQL \u6e05\u5355<\/h2>\n\n\n<h3 class=\"wp-block-heading\">28.1 goods_service Mapper SQL<\/h3>\n\n\n<p class=\"wp-block-paragraph\"><strong>GoodsMapper.xml<\/strong>\uff08\u5b8c\u6574\u7248\uff0c\u5173\u952eSQL\uff09\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.goods_service.mapper.GoodsMapper\"&gt;\n\n    &lt;resultMap id=\"goodsMap\" type=\"goods\"&gt;\n        &lt;id column=\"id\" property=\"id\"\/&gt;\n        &lt;result column=\"category_id\" property=\"categoryId\"\/&gt;\n        &lt;association column=\"category_id\" property=\"category\" javaType=\"category\"\n                     select=\"org.example.mall.goods_service.mapper.CategoryMapper.selectById\"\/&gt;\n        &lt;collection column=\"id\" property=\"picList\" ofType=\"goodsPic\"\n                    select=\"org.example.mall.goods_service.mapper.GoodsPicMapper.selectByGoodsId\"\/&gt;\n    &lt;\/resultMap&gt;\n\n    &lt;insert id=\"insert\" parameterType=\"goods\" useGeneratedKeys=\"true\" keyProperty=\"id\"&gt;\n        INSERT INTO goods(name,decp,detail,price,market_price,purchase_price,\n                          color,version,count,recom,category_id,score,status)\n        VALUES (#{name},#{decp},#{detail},#{price},#{marketPrice},#{purchasePrice},\n                #{color},#{version},#{count},#{recom},#{categoryId},#{score},#{status})\n    &lt;\/insert&gt;\n\n    &lt;update id=\"update\" parameterType=\"goods\"&gt;\n        UPDATE goods\n        &lt;set&gt;\n            &lt;if test=\"name!=null\"&gt;name=#{name},&lt;\/if&gt;\n            &lt;if test=\"decp!=null\"&gt;decp=#{decp},&lt;\/if&gt;\n            &lt;if test=\"detail!=null\"&gt;detail=#{detail},&lt;\/if&gt;\n            &lt;if test=\"price!=null\"&gt;price=#{price},&lt;\/if&gt;\n            &lt;if test=\"marketPrice!=null\"&gt;market_price=#{marketPrice},&lt;\/if&gt;\n            &lt;if test=\"purchasePrice!=null\"&gt;purchase_price=#{purchasePrice},&lt;\/if&gt;\n            &lt;if test=\"color!=null\"&gt;color=#{color},&lt;\/if&gt;\n            &lt;if test=\"version!=null\"&gt;version=#{version},&lt;\/if&gt;\n            &lt;if test=\"count!=null\"&gt;count=#{count},&lt;\/if&gt;\n            &lt;if test=\"recom!=null\"&gt;recom=#{recom},&lt;\/if&gt;\n            &lt;if test=\"categoryId!=null\"&gt;category_id=#{categoryId},&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;status=#{status},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id=#{id}\n    &lt;\/update&gt;\n\n    &lt;delete id=\"delete\"&gt;DELETE FROM goods WHERE id=#{id}&lt;\/delete&gt;\n\n    &lt;select id=\"selectByid\" resultMap=\"goodsMap\"&gt;\n        SELECT * FROM goods WHERE id=#{id}\n    &lt;\/select&gt;\n\n    &lt;select id=\"selectByCondition\" resultMap=\"goodsMap\"&gt;\n        SELECT * FROM goods\n        &lt;where&gt;\n            &lt;if test=\"name!=null\"&gt;AND name LIKE CONCAT('%',#{name},'%')&lt;\/if&gt;\n            &lt;if test=\"categoryId!=null\"&gt;AND category_id=#{categoryId}&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;AND status=#{status}&lt;\/if&gt;\n        &lt;\/where&gt;\n    &lt;\/select&gt;\n\n    &lt;select id=\"selectByCondition1\" resultMap=\"goodsMap\"&gt;\n        SELECT g.* FROM goods g, category c\n        &lt;where&gt;\n            g.category_id=c.id\n            &lt;if test=\"name!=null\"&gt;AND g.name LIKE CONCAT('%',#{name},'%')&lt;\/if&gt;\n            &lt;if test=\"categoryId!=null\"&gt;AND (c.parent_id=#{categoryId} OR c.id=#{categoryId})&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;AND g.status=#{status}&lt;\/if&gt;\n            &lt;if test=\"recom!=null\"&gt;AND g.recom=#{recom}&lt;\/if&gt;\n        &lt;\/where&gt;\n    &lt;\/select&gt;\n\n    &lt;select id=\"selectByCategoryId\" resultMap=\"goodsMap\"&gt;\n        SELECT g.* FROM goods g, category c\n        WHERE g.category_id=c.id AND g.status=1 AND (c.parent_id=#{categoryId} OR c.id=#{categoryId})\n    &lt;\/select&gt;\n\n    &lt;!-- \u6263\u51cf\u5e93\u5b58\uff1aWHERE count&gt;=#{count} \u9632\u8d85\u5356 --&gt;\n    &lt;update id=\"reduceStock\"&gt;\n        UPDATE goods SET count = count - #{count} WHERE id = #{id} AND count &gt;= #{count}\n    &lt;\/update&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>GoodsPicMapper.xml<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.goods_service.mapper.GoodsPicMapper\"&gt;\n    &lt;insert id=\"insert\"&gt;\n        INSERT INTO goods_pic(url, goods_id) VALUES\n        &lt;foreach collection=\"picList\" item=\"pic\" separator=\",\"&gt;\n            (#{pic.url}, #{pic.goodsId})\n        &lt;\/foreach&gt;\n    &lt;\/insert&gt;\n    &lt;delete id=\"delete\"&gt;DELETE FROM goods_pic WHERE goods_id = #{goodsId}&lt;\/delete&gt;\n    &lt;select id=\"selectByGoodsId\" resultType=\"org.example.mall.common.bean.GoodsPic\"&gt;\n        SELECT * FROM goods_pic WHERE goods_id = #{goodsId}\n    &lt;\/select&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>CategoryMapper.xml<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.goods_service.mapper.CategoryMapper\"&gt;\n    &lt;insert id=\"insert\" parameterType=\"category\" useGeneratedKeys=\"true\" keyProperty=\"id\"&gt;\n        INSERT INTO category(name, dscp, pic, parent_id, recom, status)\n        VALUES (#{name}, #{dscp}, #{pic}, #{parentId}, #{recom}, #{status})\n    &lt;\/insert&gt;\n    &lt;update id=\"update\" parameterType=\"category\"&gt;\n        UPDATE category\n        &lt;set&gt;\n            &lt;if test=\"name!=null\"&gt;name=#{name},&lt;\/if&gt;\n            &lt;if test=\"dscp!=null\"&gt;dscp=#{dscp},&lt;\/if&gt;\n            &lt;if test=\"pic!=null\"&gt;pic=#{pic},&lt;\/if&gt;\n            &lt;if test=\"parentId!=null\"&gt;parent_id=#{parentId},&lt;\/if&gt;\n            &lt;if test=\"recom!=null\"&gt;recom=#{recom},&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;status=#{status},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id=#{id}\n    &lt;\/update&gt;\n    &lt;delete id=\"delete\"&gt;DELETE FROM category WHERE id=#{id}&lt;\/delete&gt;\n    &lt;select id=\"selectById\" resultType=\"category\"&gt;SELECT * FROM category WHERE id=#{id}&lt;\/select&gt;\n    &lt;select id=\"selectAllParent\" resultType=\"category\"&gt;\n        SELECT * FROM category WHERE parent_id=0 AND status=1\n    &lt;\/select&gt;\n    &lt;select id=\"selectByCondition\" resultType=\"category\"&gt;\n        SELECT * FROM category\n        &lt;where&gt;\n            &lt;if test=\"name!=null\"&gt;AND name LIKE CONCAT('%',#{name},'%')&lt;\/if&gt;\n            &lt;if test=\"parentId!=null\"&gt;AND parent_id=#{parentId}&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;AND status=#{status}&lt;\/if&gt;\n        &lt;\/where&gt;\n        ORDER BY parent_id, id\n    &lt;\/select&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">28.2 user_service Mapper SQL<\/h3>\n\n\n<p class=\"wp-block-paragraph\"><strong>UserMapper.xml<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.user_service.mapper.UserMapper\"&gt;\n    &lt;insert id=\"insert\" parameterType=\"user\"&gt;\n        INSERT INTO user(username,password,salt,realname,gender,phone,email,money,status)\n        VALUES (#{username},#{password},#{salt},#{realname},#{gender},#{phone},#{email},#{money},#{status})\n    &lt;\/insert&gt;\n    &lt;update id=\"updateById\" parameterType=\"user\"&gt;\n        UPDATE user\n        &lt;set&gt;\n            &lt;if test=\"password!=null\"&gt;password=#{password},&lt;\/if&gt;\n            &lt;if test=\"salt!=null\"&gt;salt=#{salt},&lt;\/if&gt;\n            &lt;if test=\"realname!=null\"&gt;realname=#{realname},&lt;\/if&gt;\n            &lt;if test=\"gender!=null\"&gt;gender=#{gender},&lt;\/if&gt;\n            &lt;if test=\"idCard!=null\"&gt;id_card=#{idCard},&lt;\/if&gt;\n            &lt;if test=\"phone!=null\"&gt;phone=#{phone},&lt;\/if&gt;\n            &lt;if test=\"email!=null\"&gt;email=#{email},&lt;\/if&gt;\n            &lt;if test=\"money!=null\"&gt;money=#{money},&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;status=#{status},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id=#{id}\n    &lt;\/update&gt;\n    &lt;delete id=\"delete\"&gt;DELETE FROM user WHERE id=#{id}&lt;\/delete&gt;\n    &lt;select id=\"selectById\" resultType=\"user\"&gt;SELECT * FROM user WHERE id=#{id}&lt;\/select&gt;\n    &lt;select id=\"selectByUsername\" resultType=\"user\"&gt;SELECT * FROM user WHERE username=#{username}&lt;\/select&gt;\n    &lt;select id=\"selectByCondition\" resultType=\"user\"&gt;\n        SELECT * FROM user\n        &lt;where&gt;\n            &lt;if test=\"username!=null\"&gt;AND username LIKE CONCAT('%',#{username},'%')&lt;\/if&gt;\n            &lt;if test=\"phone!=null\"&gt;AND phone LIKE CONCAT('%',#{phone},'%')&lt;\/if&gt;\n            &lt;if test=\"email!=null\"&gt;AND email LIKE CONCAT('%',#{email},'%')&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;AND status=#{status}&lt;\/if&gt;\n        &lt;\/where&gt;\n    &lt;\/select&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>CollectMapper.xml<\/strong>\uff08\u5b8c\u6574\u7248\uff09\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.user_service.mapper.CollectMapper\"&gt;\n\n    &lt;resultMap id=\"collectWithGoods\" type=\"collect\"&gt;\n        &lt;id column=\"c_id\" property=\"id\"\/&gt;\n        &lt;result column=\"goods_id\" property=\"goodsId\"\/&gt;\n        &lt;result column=\"user_id\" property=\"userId\"\/&gt;\n        &lt;result column=\"c_create_time\" property=\"createTime\"\/&gt;\n        &lt;association property=\"goods\" javaType=\"org.example.mall.common.bean.Goods\"&gt;\n            &lt;id column=\"g_id\" property=\"id\"\/&gt;\n            &lt;result column=\"g_name\" property=\"name\"\/&gt;\n            &lt;result column=\"g_price\" property=\"price\"\/&gt;\n            &lt;result column=\"g_market_price\" property=\"marketPrice\"\/&gt;\n            &lt;result column=\"g_status\" property=\"status\"\/&gt;\n            &lt;result column=\"g_color\" property=\"color\"\/&gt;\n            &lt;result column=\"g_version\" property=\"version\"\/&gt;\n            &lt;result column=\"g_count\" property=\"count\"\/&gt;\n            &lt;collection property=\"picList\" ofType=\"org.example.mall.common.bean.GoodsPic\"&gt;\n                &lt;id column=\"gp_id\" property=\"id\"\/&gt;\n                &lt;result column=\"gp_url\" property=\"url\"\/&gt;\n                &lt;result column=\"gp_goods_id\" property=\"goodsId\"\/&gt;\n            &lt;\/collection&gt;\n        &lt;\/association&gt;\n    &lt;\/resultMap&gt;\n\n    &lt;insert id=\"insert\" parameterType=\"collect\"&gt;\n        INSERT INTO collect(goods_id, user_id) VALUES (#{goodsId}, #{userId})\n    &lt;\/insert&gt;\n\n    &lt;delete id=\"delect\"&gt;DELETE FROM collect WHERE id=#{id}&lt;\/delete&gt;\n\n    &lt;select id=\"selectById\" resultType=\"collect\"&gt;SELECT * FROM collect WHERE id=#{id}&lt;\/select&gt;\n\n    &lt;select id=\"selectByGoodsIdAndUserId\" resultType=\"collect\"&gt;\n        SELECT * FROM collect WHERE goods_id=#{goodsId} AND user_id=#{userId}\n    &lt;\/select&gt;\n\n    &lt;!-- LEFT JOIN\u5e72\u4e09\u8868\uff0c\u4e00\u6b21SQL\u62ff\u56de\u6536\u85cf+\u5546\u54c1+\u56fe\u7247 --&gt;\n    &lt;select id=\"selectByUserId\" resultMap=\"collectWithGoods\"&gt;\n        SELECT c.id AS c_id, c.goods_id, c.user_id, c.create_time AS c_create_time,\n               g.id AS g_id, g.name AS g_name, g.price AS g_price,\n               g.market_price AS g_market_price, g.status AS g_status,\n               g.color AS g_color, g.version AS g_version, g.count AS g_count,\n               gp.id AS gp_id, gp.url AS gp_url, gp.goods_id AS gp_goods_id\n        FROM collect c\n        LEFT JOIN goods g ON c.goods_id = g.id\n        LEFT JOIN goods_pic gp ON g.id = gp.goods_id\n        WHERE c.user_id = #{userId}\n        ORDER BY c.create_time DESC\n    &lt;\/select&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>AddrMapper.xml<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.user_service.mapper.AddrMapper\"&gt;\n    &lt;insert id=\"insert\" parameterType=\"addr\"&gt;\n        INSERT INTO addr(contact,phone,province,city,district,street,address,user_id,status)\n        VALUES (#{contact},#{phone},#{province},#{city},#{district},#{street},#{address},#{userId},#{status})\n    &lt;\/insert&gt;\n    &lt;update id=\"update\" parameterType=\"addr\"&gt;\n        UPDATE addr\n        &lt;set&gt;\n            &lt;if test=\"contact!=null and contact!=''\"&gt;contact=#{contact},&lt;\/if&gt;\n            &lt;if test=\"phone!=null and phone!=''\"&gt;phone=#{phone},&lt;\/if&gt;\n            &lt;if test=\"province!=null\"&gt;province=#{province},&lt;\/if&gt;\n            &lt;if test=\"city!=null\"&gt;city=#{city},&lt;\/if&gt;\n            &lt;if test=\"district!=null\"&gt;district=#{district},&lt;\/if&gt;\n            &lt;if test=\"street!=null\"&gt;street=#{street},&lt;\/if&gt;\n            &lt;if test=\"address!=null and address!=''\"&gt;address=#{address},&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;status=#{status},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id=#{id}\n    &lt;\/update&gt;\n    &lt;delete id=\"delete\"&gt;DELETE FROM addr WHERE id=#{id}&lt;\/delete&gt;\n    &lt;select id=\"selectById\" resultType=\"addr\"&gt;SELECT * FROM addr WHERE id=#{id}&lt;\/select&gt;\n    &lt;select id=\"selectByUserId\" resultType=\"addr\"&gt;\n        SELECT * FROM addr WHERE user_id=#{userId} ORDER BY status DESC, id DESC\n    &lt;\/select&gt;\n    &lt;update id=\"clearDefault\"&gt;UPDATE addr SET status=0 WHERE user_id=#{userId}&lt;\/update&gt;\n    &lt;update id=\"setDefault\"&gt;UPDATE addr SET status=1 WHERE id=#{id}&lt;\/update&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">28.3 admin_service Mapper SQL<\/h3>\n\n\n<p class=\"wp-block-paragraph\"><strong>AdminMapper.xml<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.admin_service.mapper.AdminMapper\"&gt;\n    &lt;insert id=\"insert\" parameterType=\"admin\"&gt;\n        INSERT INTO admin(username,password,salt,phone,email,realname,status)\n        VALUES (#{username},#{password},#{salt},#{phone},#{email},#{realname},#{status})\n    &lt;\/insert&gt;\n    &lt;update id=\"update\" parameterType=\"admin\"&gt;\n        UPDATE admin\n        &lt;set&gt;\n            &lt;if test=\"password!=null\"&gt;password=#{password},&lt;\/if&gt;\n            &lt;if test=\"salt!=null\"&gt;salt=#{salt},&lt;\/if&gt;\n            &lt;if test=\"phone!=null\"&gt;phone=#{phone},&lt;\/if&gt;\n            &lt;if test=\"email!=null\"&gt;email=#{email},&lt;\/if&gt;\n            &lt;if test=\"realname!=null\"&gt;realname=#{realname},&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;status=#{status},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id=#{id}\n    &lt;\/update&gt;\n    &lt;delete id=\"delete\"&gt;DELETE FROM admin WHERE id=#{id}&lt;\/delete&gt;\n    &lt;select id=\"selectById\" resultType=\"admin\"&gt;SELECT * FROM admin WHERE id=#{id}&lt;\/select&gt;\n    &lt;select id=\"selectByUsername\" resultType=\"admin\"&gt;SELECT * FROM admin WHERE username=#{username}&lt;\/select&gt;\n    &lt;select id=\"selectByCondition\" resultType=\"admin\"&gt;\n        SELECT * FROM admin\n        &lt;where&gt;\n            &lt;if test=\"username!=null\"&gt;AND username LIKE CONCAT('%',#{username},'%')&lt;\/if&gt;\n            &lt;if test=\"phone!=null\"&gt;AND phone LIKE CONCAT('%',#{phone},'%')&lt;\/if&gt;\n            &lt;if test=\"status!=null\"&gt;AND status=#{status}&lt;\/if&gt;\n        &lt;\/where&gt;\n    &lt;\/select&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">28.4 order_service Mapper SQL<\/h3>\n\n\n<p class=\"wp-block-paragraph\"><strong>CartMapper.xml<\/strong>\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.order_service.mapper.CartMapper\"&gt;\n    &lt;insert id=\"insert\" parameterType=\"cart\"&gt;\n        INSERT INTO cart(count, user_id, goods_id) VALUES (#{count}, #{userId}, #{goodsId})\n    &lt;\/insert&gt;\n    &lt;update id=\"update\" parameterType=\"cart\"&gt;\n        UPDATE cart\n        &lt;set&gt;\n            &lt;if test=\"count!=null\"&gt;count=#{count},&lt;\/if&gt;\n            &lt;if test=\"userId!=null\"&gt;user_id=#{userId},&lt;\/if&gt;\n            &lt;if test=\"goodsId!=null\"&gt;goods_id=#{goodsId},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id=#{id}\n    &lt;\/update&gt;\n    &lt;delete id=\"delete\"&gt;DELETE FROM cart WHERE id=#{id}&lt;\/delete&gt;\n    &lt;delete id=\"deleteByIds\"&gt;\n        DELETE FROM cart WHERE id IN\n        &lt;foreach collection=\"ids\" item=\"id\" open=\"(\" separator=\",\" close=\")\"&gt;#{id}&lt;\/foreach&gt;\n    &lt;\/delete&gt;\n    &lt;select id=\"selectById\" resultType=\"Cart\"&gt;SELECT * FROM cart WHERE id=#{id}&lt;\/select&gt;\n    &lt;select id=\"selectByGoodsIdAndUserId\" resultType=\"Cart\"&gt;\n        SELECT * FROM cart WHERE user_id=#{userId} AND goods_id=#{goodsId}\n    &lt;\/select&gt;\n    &lt;select id=\"selectByCondition\" resultType=\"org.example.mall.common.bean.Cart\"&gt;\n        SELECT * FROM cart\n        &lt;where&gt;\n            &lt;if test=\"id!=null\"&gt;AND id=#{id}&lt;\/if&gt;\n            &lt;if test=\"goodsId!=null\"&gt;AND goods_id=#{goodsId}&lt;\/if&gt;\n            &lt;if test=\"userId!=null\"&gt;AND user_id=#{userId}&lt;\/if&gt;\n        &lt;\/where&gt;\n    &lt;\/select&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>OrderMapper.xml<\/strong>\uff08\u5b8c\u6574\u7248\uff09\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>&lt;mapper namespace=\"org.example.mall.order_service.mapper.OrderMapper\"&gt;\n    &lt;resultMap id=\"orderMap\" type=\"org.example.mall.common.bean.Order\"&gt;\n        &lt;id column=\"id\" property=\"id\"\/&gt;\n        &lt;result column=\"user_id\" property=\"userId\"\/&gt;\n        &lt;result column=\"pay_type\" property=\"payType\"\/&gt;\n        &lt;result column=\"express\" property=\"express\"\/&gt;\n        &lt;result column=\"addr_id\" property=\"addrId\"\/&gt;\n        &lt;result column=\"addr_detail\" property=\"addrDetail\"\/&gt;\n        &lt;result column=\"status\" property=\"status\"\/&gt;\n        &lt;result column=\"items\" property=\"items\"\/&gt;\n        &lt;result column=\"create_time\" property=\"createTime\"\/&gt;\n        &lt;result column=\"username\" property=\"username\"\/&gt;\n    &lt;\/resultMap&gt;\n\n    &lt;insert id=\"insert\" parameterType=\"org.example.mall.common.bean.Order\"&gt;\n        INSERT INTO `order`(id, user_id, pay_type, express, addr_id, addr_detail, status, items, create_time)\n        VALUES (#{id}, #{userId}, #{payType}, #{express}, #{addrId}, #{addrDetail}, #{status}, #{items}, NOW())\n    &lt;\/insert&gt;\n\n    &lt;select id=\"selectByCondition\" resultMap=\"orderMap\"&gt;\n        SELECT o.*, u.username FROM `order` o LEFT JOIN `user` u ON o.user_id = u.id\n        &lt;where&gt;\n            &lt;if test=\"condition.userId!=null\"&gt;AND o.user_id = #{condition.userId}&lt;\/if&gt;\n            &lt;if test=\"condition.status!=null\"&gt;AND o.status = #{condition.status}&lt;\/if&gt;\n        &lt;\/where&gt;\n        ORDER BY o.create_time DESC\n    &lt;\/select&gt;\n\n    &lt;select id=\"selectById\" resultMap=\"orderMap\"&gt;\n        SELECT o.*, u.username FROM `order` o LEFT JOIN `user` u ON o.user_id = u.id WHERE o.id = #{id}\n    &lt;\/select&gt;\n\n    &lt;select id=\"selectByUserId\" resultMap=\"orderMap\"&gt;\n        SELECT o.*, u.username FROM `order` o LEFT JOIN `user` u ON o.user_id = u.id\n        WHERE o.user_id = #{userId} ORDER BY o.create_time DESC\n    &lt;\/select&gt;\n\n    &lt;select id=\"selectAddrById\" resultType=\"org.example.mall.common.bean.Addr\"&gt;\n        SELECT * FROM addr WHERE id = #{id}\n    &lt;\/select&gt;\n\n    &lt;update id=\"updateById\"&gt;\n        UPDATE `order`\n        &lt;set&gt;\n            &lt;if test=\"status!=null\"&gt;status=#{status},&lt;\/if&gt;\n            &lt;if test=\"express!=null and express!=''\"&gt;express=#{express},&lt;\/if&gt;\n            &lt;if test=\"addrDetail!=null and addrDetail!=''\"&gt;addr_detail=#{addrDetail},&lt;\/if&gt;\n            &lt;if test=\"items!=null\"&gt;items=#{items},&lt;\/if&gt;\n        &lt;\/set&gt;\n        WHERE id = #{id}\n    &lt;\/update&gt;\n\n    &lt;delete id=\"delete\"&gt;DELETE FROM `order` WHERE id = #{id}&lt;\/delete&gt;\n\n    &lt;update id=\"cancelExpiredOrders\"&gt;\n        UPDATE `order` SET status = 4 WHERE status = 0 AND create_time &amp;lt; #{before}\n    &lt;\/update&gt;\n&lt;\/mapper&gt;<\/code><\/pre>\n\n<hr \/>\n\n<h2 class=\"wp-block-heading\">29. \u90e8\u7f72\u8be6\u7ec6\u6b65\u9aa4<\/h2>\n\n\n<h3 class=\"wp-block-heading\">29.1 \u73af\u5883\u53d8\u91cf\u6ce8\u5165\u65b9\u5f0f<\/h3>\n\n\n<p class=\"wp-block-paragraph\"><strong>\u65b9\u5f0f\u4e00\uff1aexport\uff08\u5f53\u524d\u7ec8\u7aef\u6709\u6548\uff09<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\"><code>export ALIPAY_APP_ID=9021000165698953\nexport ALIPAY_PRIVATE_KEY=\"MIIEvAIBADAN...\"\nexport ALIPAY_PUBLIC_KEY=\"MIIBIjAN...\"\nexport ALIPAY_RETURN_URL=http:\/\/\u4f60\u7684IP\/#\/user\/payResult\nexport ALIPAY_NOTIFY_URL=http:\/\/\u4f60\u7684IP\/order\/notify\njava -jar order_service\/target\/order_service-0.0.1-SNAPSHOT.jar<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>\u65b9\u5f0f\u4e8c\uff1asystemd\u670d\u52a1\uff08Linux\u957f\u671f\u8fd0\u884c\u63a8\u8350\uff09<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\"><code>[Unit]\nDescription=Mall Order Service\n[Service]\nUser=mall\nWorkingDirectory=\/opt\/mall\nExecStart=\/usr\/bin\/java -jar \/opt\/mall\/order_service.jar\nEnvironment=\"ALIPAY_APP_ID=9021000165698953\"\nEnvironment=\"ALIPAY_PRIVATE_KEY=MIIEvAIBADANBgkqh...\"\nEnvironment=\"ALIPAY_PUBLIC_KEY=MIIBIjANBgkqhkiG9w0BAQEF...\"\nEnvironment=\"ALIPAY_RETURN_URL=http:\/\/\u4f60\u7684\u57df\u540d\/#\/user\/payResult\"\nEnvironment=\"ALIPAY_NOTIFY_URL=http:\/\/\u4f60\u7684\u57df\u540d\/order\/notify\"\nRestart=always\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>\u65b9\u5f0f\u4e09\uff1aWindows\u6279\u5904\u7406<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\"><code>set ALIPAY_APP_ID=9021000165698953\nset ALIPAY_PRIVATE_KEY=MIIEvAIBADANBgkqhkiG...\nset ALIPAY_PUBLIC_KEY=MIIBIjANBgkqhkiG9w0BAQEF...\nset ALIPAY_RETURN_URL=http:\/\/\u4f60\u7684IP\/#\/user\/payResult\nset ALIPAY_NOTIFY_URL=http:\/\/\u4f60\u7684IP\/order\/notify\njava -jar order_service-0.0.1-SNAPSHOT.jar<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">29.2 JVM\u542f\u52a8\u53c2\u6570\u4f18\u5316<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>java -Xms256m -Xmx512m -XX:+UseG1GC -jar xxx.jar<\/code><\/pre>\n\n\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>\u53c2\u6570<\/th>\n<th>\u503c<\/th>\n<th>\u8bf4\u660e<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>-Xms<\/code><\/td>\n<td>256m<\/td>\n<td>\u521d\u59cb\u5806\u5185\u5b58<\/td>\n<\/tr>\n<tr>\n<td><code>-Xmx<\/code><\/td>\n<td>512m<\/td>\n<td>\u6700\u5927\u5806\u5185\u5b58<\/td>\n<\/tr>\n<tr>\n<td><code>-XX:+UseG1GC<\/code><\/td>\n<td>&#8211;<\/td>\n<td>\u4f7f\u7528G1\u5783\u573e\u56de\u6536\u5668<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n\n\n<h3 class=\"wp-block-heading\">29.3 nginx\u524d\u7aef\u90e8\u7f72<\/h3>\n\n\n<pre class=\"wp-block-code\"><code># 1. \u590d\u5236\u524d\u7aef\u4ea7\u7269\u7684\u9759\u6001\u6587\u4ef6\ncp -r front\/dist\/* \/usr\/share\/nginx\/html\/\n\n# 2. \u590d\u5236nginx\u914d\u7f6e\u6587\u4ef6\ncp nginx\/conf\/nginx.conf \/etc\/nginx\/nginx.conf\n\n# 3. \u6d4b\u8bd5\u914d\u7f6e+\u91cd\u8f7d\nnginx -t\nnginx -s reload<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">29.4 \u6253\u5305\u811a\u672c\uff08\u4e00\u952e\u6253\u5305\u524d\u540e\u7aef\uff09<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# build.sh\nset -e\n\necho \"=== \u6253\u5305\u540e\u7aef ===\"\nmvn clean package -DskipTests -q\necho \"\u540e\u7aef\u6253\u5305\u5b8c\u6210\"\n\necho \"=== \u6253\u5305\u524d\u7aef ===\"\ncd front\nnpm run build\ncd ..\necho \"\u524d\u7aef\u6253\u5305\u5b8c\u6210\"\n\necho \"\u4ea7\u7269:\"\necho \"  \u524d\u7aef: front\/dist\/\"\necho \"  \u540e\u7aef:\"\nls -lh *\/target\/*.jar<\/code><\/pre>\n\n<hr \/>\n\n<h2 class=\"wp-block-heading\">30. Dubbo \u542f\u52a8\u9a8c\u8bc1<\/h2>\n\n\n<h3 class=\"wp-block-heading\">30.1 \u786e\u8ba4 ZooKeeper \u6ce8\u518c\u6210\u529f<\/h3>\n\n\n<p class=\"wp-block-paragraph\">\u542f\u52a8 goods_service \u540e\uff0c\u7528 ZooKeeper \u5ba2\u6237\u7aef\u68c0\u67e5\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code># \u8fde\u63a5 ZooKeeper\ncd zookeeper-x.x.x\nbin\/zkCli.cmd -server 127.0.0.1:2181\n\n# \u67e5\u770b\u6ce8\u518c\u7684 Dubbo \u670d\u52a1\nls \/dubbo\/org.example.mall.common.service.GoodsService\/providers\n\n# \u5e94\u8be5\u770b\u5230\u4e00\u4e2a URL \u7f16\u7801\u7684\u5730\u5740\uff0c\u5982\uff1a\n# [dubbo:\/\/192.168.1.5:38090\/org.example.mall.common.service.GoodsService?...]<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u5982\u679c\u80fd\u770b\u5230 provider \u5730\u5740\uff0c\u8bf4\u660e goods_service \u5df2\u6210\u529f\u6ce8\u518c\u5230 ZooKeeper\uff0c\u53ef\u4ee5\u88ab order_service \u8c03\u7528\u4e86\u3002<\/p>\n\n\n<h3 class=\"wp-block-heading\">30.2 \u9a8c\u8bc1 Dubbo \u8c03\u7528<\/h3>\n\n\n<p class=\"wp-block-paragraph\">\u542f\u52a8 order_service \u540e\uff0c\u770b\u542f\u52a8\u65e5\u5fd7\uff1a<\/p>\n\n\n<pre class=\"wp-block-code\"><code>[Dubbo] Registering consumer for service org.example.mall.common.service.GoodsService<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">\u8868\u793a order_service \u4f5c\u4e3a\u6d88\u8d39\u8005\u5df2\u8fde\u4e0a goods_service\u3002<\/p>\n\n\n<p class=\"wp-block-paragraph\">\u5b9e\u9645\u8c03\u7528\u6d4b\u8bd5\uff1a\u8fdb\u5165\u524d\u53f0\u2192\u52a0\u5165\u8d2d\u7269\u8f66\u2192\u70b9\u51fb\u7ed3\u7b97\u2192\u5982\u679c\u770b\u5230\u5546\u54c1\u6e05\u5355\uff0c\u8bf4\u660e Dubbo \u8c03\u7528\u6210\u529f\uff08order_service \u901a\u8fc7 Dubbo \u4ece goods_service \u67e5\u5230\u4e86\u5546\u54c1\u4fe1\u606f\uff09\u3002<\/p>\n\n\n<h3 class=\"wp-block-heading\">30.3 \u5e38\u89c1 Dubbo \u542f\u52a8\u95ee\u9898<\/h3>\n\n\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>\u73b0\u8c61<\/th>\n<th>\u539f\u56e0<\/th>\n<th>\u89e3\u51b3<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>Failed to connect to \/127.0.0.1:2181<\/code><\/td>\n<td>ZooKeeper\u8fd8\u6ca1\u542f\u52a8<\/td>\n<td>\u5148\u542f\u52a8zkServer.cmd<\/td>\n<\/tr>\n<tr>\n<td><code>No provider available<\/code><\/td>\n<td>goods_service\u6ca1\u6ce8\u518c\u6210\u529f<\/td>\n<td>\u68c0\u67e5 goods_service \u65e5\u5fd7\u3001@EnableDubbo\u662f\u5426\u6b63\u786e\u3001application.yml dubbo\u914d\u7f6e<\/td>\n<\/tr>\n<tr>\n<td><code>Connection refused<\/code><\/td>\n<td>Dubbo\u7aef\u53e3\u88ab\u9632\u706b\u5899\u62e6<\/td>\n<td>\u5f00\u653e38090\u7b49\u7aef\u53e3\u6216\u5173\u95ed\u9632\u706b\u5899\u6d4b\u8bd5<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n\n<hr \/>\n\n<h2 class=\"wp-block-heading\">31. \u5b8c\u6574 API \u54cd\u5e94\u793a\u4f8b<\/h2>\n\n\n<h3 class=\"wp-block-heading\">31.1 \u4e0b\u5355\u6210\u529f\u54cd\u5e94<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"code\": 200,\n  \"msg\": \"\u4e0b\u5355\u6210\u529f\",\n  \"data\": {\n    \"id\": \"1876543210987654321\",\n    \"userId\": 1,\n    \"payType\": 1,\n    \"addrId\": 1,\n    \"addrDetail\": \"\u5c71\u4e1c\u7701\u9752\u5c9b\u5e02\u57ce\u9633\u533a\u57ce\u9633\u8857\u9053 \u519c\u5927\u897f\u82d1A\u5ea7 - \u6c64\u59c6 - 13322332233\",\n    \"status\": 0,\n    \"items\": \"[{\"goodsId\":45,\"goodsName\":\"\u7d22\u5c3c WH-1000XM5\",\"goodsPic\":\"goods_045_main.jpg\",\"price\":2399.00,\"count\":2,\"subtotal\":4798.00}]\",\n    \"createTime\": \"2026-07-28 15:30:00\",\n    \"detailList\": [\n      {\n        \"goodsId\": 45,\n        \"goodsName\": \"\u7d22\u5c3c WH-1000XM5\",\n        \"goodsPic\": \"goods_045_main.jpg\",\n        \"price\": 2399.00,\n        \"count\": 2,\n        \"subtotal\": 4798.00\n      }\n    ],\n    \"totalPrice\": 4798.00\n  }\n}<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">31.2 \u652f\u4ed8\u8868\u5355\u54cd\u5e94<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"code\": 200,\n  \"msg\": \"\",\n  \"data\": \"&lt;form name=\"punchout_form\" method=\"post\" action=\"https:\/\/openapi-sandbox.dl.alipaydev.com\/gateway.do?charset=utf-8&amp;method=alipay.trade.page.pay&amp;sign=...\"&gt;&lt;input type=\"hidden\" name=\"biz_content\" value=\"{...}\"\/&gt;&lt;input type=\"submit\" value=\"submit\" style=\"display:none\"\/&gt;&lt;\/form&gt;&lt;script&gt;document.forms[0].submit();&lt;\/script&gt;\"\n}<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">31.3 \u4eea\u8868\u76d8\u54cd\u5e94<\/h3>\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"code\": 200,\n  \"msg\": \"\",\n  \"data\": {\n    \"totalOrders\": 100,\n    \"totalUsers\": 50,\n    \"totalGoods\": 30,\n    \"todayOrders\": 5,\n    \"todaySales\": 15997.00,\n    \"statusCnt\": {\n      \"pending\": 10,\n      \"paid\": 80,\n      \"shipped\": 3,\n      \"completed\": 5,\n      \"cancelled\": 2\n    },\n    \"recentOrders\": [\n      {\"id\": \"1876543210987654321\", \"userId\": 1, \"status\": 1, \"createTime\": \"2026-07-28 15:30:00\", \"username\": \"tom\"}\n    ],\n    \"recentUsers\": [\n      {\"id\": 5, \"username\": \"cindy\", \"phone\": \"15588889999\", \"email\": \"cindy@mall-demo.com\", \"regTime\": \"2025-01-05 12:00:00\", \"status\": 0}\n    ]\n  }\n}<\/code><\/pre>\n\n<hr \/>\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n\n\n<p class=\"wp-block-paragraph\"><strong>\u81f3\u6b64\uff0c\u4e09\u4efd\u6587\u6863\u5b8c\u6574\u8986\u76d6\u4e86\u9879\u76ee\u7684\uff1a<\/strong><\/p>\n\n\n<ul class=\"wp-block-list\">\n\n<li>\u5168\u90e8\u529f\u80fd\u539f\u7406 +\u4ee3\u7801\u9010\u884c\u6ce8\u91ca\uff08PROJECT_DOC.md \u7b2c1-18\u7ae0\uff09<\/li>\n<li>\u5168\u90e8\u73af\u5883\u642d\u5efa + \u914d\u7f6e + \u90e8\u7f72\u6b65\u9aa4\uff08DOC_APPENDIX.md \u7b2c19-25\u7ae0\uff09<\/li>\n<li>\u5168\u90e8\u7f3a\u5931\u4ee3\u7801 + Mapper XML + \u524d\u7aef\u9875\u9762 + \u5de5\u5177\u7c7b + API\u793a\u4f8b + Dubbo\u9a8c\u8bc1\uff08\u672c\u6587\u6863\u7b2c26-31\u7ae0\uff09<\/li>\n\n<\/ul>\n\n\n<p class=\"wp-block-paragraph\">\u4e09\u4efd\u6587\u6863\u52a0\u8d77\u6765\u53ef\u4ee5\uff1a\u7406\u89e3\u6bcf\u4e00\u884c\u4ee3\u7801 \u2192 \u4ece\u96f6\u642d\u5efa\u73af\u5883 \u2192 \u5199\u51fa\u5168\u90e8\u6587\u4ef6 \u2192 \u90e8\u7f72\u8fd0\u884c \u2192 \u9a8c\u8bc1\u529f\u80fd<\/p>\n\n\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u90e8\u5206\u8865\u9f50 PROJECT_DOC.md \u548c DOC_APPENDIX.md \u4e2d\u7f3a\u5931\u7684\u6240\u6709\u5185\u5bb9\uff1a \u524d\u7aef\u5168\u90e8\u9875\u9762 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-27","post","type-post","status-publish","format-standard","hentry","category-mall"],"_links":{"self":[{"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/posts\/27","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/comments?post=27"}],"version-history":[{"count":1,"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/posts\/27\/revisions"}],"predecessor-version":[{"id":28,"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/posts\/27\/revisions\/28"}],"wp:attachment":[{"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/media?parent=27"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/categories?post=27"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dxkua.cn\/index.php\/wp-json\/wp\/v2\/tags?post=27"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}