parent
b4b874cdf2
commit
831acc6a25
|
|
@ -7,6 +7,26 @@
|
||||||
- **Base URL**: `http://localhost:8080` (开发环境) / 生产环境使用相对路径
|
- **Base URL**: `http://localhost:8080` (开发环境) / 生产环境使用相对路径
|
||||||
- **Content-Type**: `application/json`
|
- **Content-Type**: `application/json`
|
||||||
- **超时时间**: 60秒
|
- **超时时间**: 60秒
|
||||||
|
- **调试模式**: 通过配置文件中的 `debug` 字段控制,默认为 `true`
|
||||||
|
|
||||||
|
## UUID关联机制
|
||||||
|
|
||||||
|
本系统使用UUID来跟踪用户的完整操作流程:
|
||||||
|
|
||||||
|
1. **页面访问** (`/api/page-visit`): 用户首次访问页面时生成唯一的UUID
|
||||||
|
2. **对联生成** (`/api/couplets`): 使用页面访问的UUID关联海报生成操作
|
||||||
|
3. **用户信息提交** (`/api/user-info`): 使用相同的UUID关联用户信息提交
|
||||||
|
|
||||||
|
这种机制允许系统追踪从页面访问到最终用户提交的完整用户行为路径。
|
||||||
|
|
||||||
|
## 调试模式配置
|
||||||
|
|
||||||
|
系统支持通过配置文件控制Gin框架的运行模式:
|
||||||
|
|
||||||
|
- **debug: true** (默认): Gin运行在调试模式,输出详细的错误信息和日志
|
||||||
|
- **debug: false**: Gin运行在发布模式,性能更好,错误信息更简洁
|
||||||
|
|
||||||
|
建议开发环境使用 `true`,生产环境使用 `false`
|
||||||
|
|
||||||
## API端点
|
## API端点
|
||||||
|
|
||||||
|
|
@ -39,7 +59,8 @@
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"message": "Page visit recorded successfully",
|
"message": "Page visit recorded successfully",
|
||||||
"id": 123
|
"id": 123,
|
||||||
|
"uuid": "550e8400-e29b-41d4-a716-446655440000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -50,6 +71,20 @@
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**错误响应** (409 Conflict)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "该手机号码已经提交过用户信息,请勿重复提交"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**错误响应** (409 Conflict)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "该页面访问已经提交过用户信息,请勿重复提交"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**错误响应** (500 Internal Server Error)
|
**错误响应** (500 Internal Server Error)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -62,6 +97,7 @@
|
||||||
- 系统会自动记录用户的IP地址
|
- 系统会自动记录用户的IP地址
|
||||||
- 如果数据库不可用,API会返回500错误
|
- 如果数据库不可用,API会返回500错误
|
||||||
- 建议在页面加载完成后异步调用,不影响用户体验
|
- 建议在页面加载完成后异步调用,不影响用户体验
|
||||||
|
- **重要**: 返回的UUID需要保存,后续的couplets和user-info接口需要使用这个UUID进行关联
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -81,6 +117,7 @@
|
||||||
| phone | string | 是 | 用户手机号码 |
|
| phone | string | 是 | 用户手机号码 |
|
||||||
| address | string | 是 | 用户地址 |
|
| address | string | 是 | 用户地址 |
|
||||||
| msg | string | 是 | 用户留言或备注信息,最多200字 |
|
| msg | string | 是 | 用户留言或备注信息,最多200字 |
|
||||||
|
| page_visit_uuid | string | 是 | 页面访问UUID,用于关联页面访问记录 |
|
||||||
|
|
||||||
#### 请求示例
|
#### 请求示例
|
||||||
```json
|
```json
|
||||||
|
|
@ -88,7 +125,8 @@
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"phone": "13800138000",
|
"phone": "13800138000",
|
||||||
"address": "北京市朝阳区xxx街道xxx号",
|
"address": "北京市朝阳区xxx街道xxx号",
|
||||||
"msg": "希望能够获得精美礼品,谢谢!"
|
"msg": "希望能够获得精美礼品,谢谢!",
|
||||||
|
"page_visit_uuid": "550e8400-e29b-41d4-a716-446655440000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -122,6 +160,8 @@
|
||||||
- msg字段最多支持200个字符,用于用户留言或备注
|
- msg字段最多支持200个字符,用于用户留言或备注
|
||||||
- 系统会自动记录创建时间
|
- 系统会自动记录创建时间
|
||||||
- 建议在前端进行基础的数据验证(如手机号格式)
|
- 建议在前端进行基础的数据验证(如手机号格式)
|
||||||
|
- **重要**: 必须使用之前从`/api/page-visit`获得的UUID作为`page_visit_uuid`参数
|
||||||
|
- **防重复提交**: 系统会检查手机号和页面访问UUID的唯一性,如果发现重复提交会返回409错误
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -138,11 +178,13 @@
|
||||||
| 参数名 | 类型 | 必需 | 描述 |
|
| 参数名 | 类型 | 必需 | 描述 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
| title | string | 是 | 两个汉字,用于生成对联(如"新春") |
|
| title | string | 是 | 两个汉字,用于生成对联(如"新春") |
|
||||||
|
| page_visit_uuid | string | 是 | 页面访问UUID,用于关联页面访问记录 |
|
||||||
|
|
||||||
#### 请求示例
|
#### 请求示例
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"title": "新春"
|
"title": "新春",
|
||||||
|
"page_visit_uuid": "550e8400-e29b-41d4-a716-446655440000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -178,6 +220,7 @@
|
||||||
- 返回的`image_url`可以直接用于显示海报
|
- 返回的`image_url`可以直接用于显示海报
|
||||||
- 返回的`share_url`可以用于分享功能
|
- 返回的`share_url`可以用于分享功能
|
||||||
- 如果AI服务不可用,系统会使用默认对联
|
- 如果AI服务不可用,系统会使用默认对联
|
||||||
|
- **重要**: 必须使用之前从`/api/page-visit`获得的UUID作为`page_visit_uuid`参数
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch, nextTick } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
|
|
@ -35,47 +35,44 @@ const handleClose = () => {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 显示提示信息
|
||||||
const handleSubmit = () => {
|
const showToast = (title) => {
|
||||||
// 验证表单
|
nextTick(() => {
|
||||||
if (!formData.value.name || !formData.value.phone || !formData.value.address) {
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请填写完整信息',
|
title,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = () => {
|
||||||
|
console.log('提交表单', formData.value)
|
||||||
|
|
||||||
|
// 验证表单
|
||||||
|
if (!formData.value.name || !formData.value.phone || !formData.value.address || !formData.value.msg) {
|
||||||
|
showToast('请填写完整信息')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证手机号
|
// 验证手机号
|
||||||
const phoneRegex = /^1[3-9]\d{9}$/
|
const phoneRegex = /^1[3-9]\d{9}$/
|
||||||
if (!phoneRegex.test(formData.value.phone)) {
|
if (!phoneRegex.test(formData.value.phone)) {
|
||||||
uni.showToast({
|
showToast('请输入正确的手机号')
|
||||||
title: '请输入正确的手机号',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 2000
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证留言
|
// 验证留言
|
||||||
if (!formData.value.msg || formData.value.msg.trim() === '') {
|
if (!formData.value.msg || formData.value.msg.trim() === '') {
|
||||||
uni.showToast({
|
showToast('请输入留言')
|
||||||
title: '请输入留言',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 2000
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证留言长度
|
// 验证留言长度
|
||||||
const msgLength = formData.value.msg.length
|
const msgLength = formData.value.msg.length
|
||||||
if (msgLength > 200) {
|
if (msgLength > 200) {
|
||||||
uni.showToast({
|
showToast('留言最多200个汉字')
|
||||||
title: '留言最多200个汉字',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 2000
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +105,6 @@ const handleSubmit = () => {
|
||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label class="form-label">姓名:</label>
|
|
||||||
<input
|
<input
|
||||||
v-model="formData.name"
|
v-model="formData.name"
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -118,17 +114,15 @@ const handleSubmit = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label class="form-label">联系电话:</label>
|
|
||||||
<input
|
<input
|
||||||
v-model="formData.phone"
|
v-model="formData.phone"
|
||||||
type="tel"
|
type="tel"
|
||||||
placeholder="请输入您的手机号"
|
placeholder="请输入您的联系电话"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label class="form-label">联系地址:</label>
|
|
||||||
<input
|
<input
|
||||||
v-model="formData.address"
|
v-model="formData.address"
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -139,12 +133,10 @@ const handleSubmit = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<label class="form-label">留言:</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
v-model="formData.msg"
|
v-model="formData.msg"
|
||||||
placeholder="请输入您的留言"
|
placeholder="请输入您的留言"
|
||||||
class="form-textarea"
|
class="form-textarea"
|
||||||
rows="3"
|
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
></textarea>
|
></textarea>
|
||||||
<span class="char-count">{{ formData.msg.length }}/200</span>
|
<span class="char-count">{{ formData.msg.length }}/200</span>
|
||||||
|
|
@ -169,7 +161,7 @@ const handleSubmit = () => {
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
z-index: 1000;
|
z-index: 100;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +184,7 @@ const handleSubmit = () => {
|
||||||
/* 返回按钮 */
|
/* 返回按钮 */
|
||||||
.back-btn {
|
.back-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 60rpx;
|
top: 30rpx;
|
||||||
left: 30rpx;
|
left: 30rpx;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
@ -206,8 +198,8 @@ const handleSubmit = () => {
|
||||||
.title-container {
|
.title-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-top: 120rpx;
|
padding-top: 100rpx;
|
||||||
margin-bottom: 30rpx;
|
margin-bottom: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-image {
|
.title-image {
|
||||||
|
|
@ -219,7 +211,7 @@ const handleSubmit = () => {
|
||||||
.tips-container {
|
.tips-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 40rpx;
|
margin-bottom: 30rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tips-image {
|
.tips-image {
|
||||||
|
|
@ -233,18 +225,10 @@ const handleSubmit = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item {
|
.form-item {
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 16rpx;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
|
||||||
display: block;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 28rpx;
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input,
|
.form-input,
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -257,11 +241,13 @@ const handleSubmit = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input {
|
.form-input {
|
||||||
height: 80rpx;
|
height: 70rpx;
|
||||||
|
padding: 16rpx 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
min-height: 120rpx;
|
height: 150rpx;
|
||||||
|
padding: 16rpx 20rpx;
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,7 +263,7 @@ const handleSubmit = () => {
|
||||||
.submit-container {
|
.submit-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 40rpx;
|
margin-top: 30rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn-image {
|
.submit-btn-image {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ const scenes = ref([
|
||||||
id: 'dongzhimen',
|
id: 'dongzhimen',
|
||||||
name: '东直门商圈',
|
name: '东直门商圈',
|
||||||
description: '京城东部的交通枢纽和商业中心',
|
description: '京城东部的交通枢纽和商业中心',
|
||||||
image: 'https://picsum.photos/id/1019/750/1600',
|
|
||||||
interactionTip: '移动烤鸭,领取团圆福筷',
|
interactionTip: '移动烤鸭,领取团圆福筷',
|
||||||
collectedItem: '团圆福筷'
|
collectedItem: '团圆福筷'
|
||||||
},
|
},
|
||||||
|
|
@ -67,7 +66,6 @@ const scenes = ref([
|
||||||
id: 'longfusi',
|
id: 'longfusi',
|
||||||
name: '隆福寺商圈',
|
name: '隆福寺商圈',
|
||||||
description: '传统文化与现代艺术融合的潮流地标',
|
description: '传统文化与现代艺术融合的潮流地标',
|
||||||
image: 'https://picsum.photos/id/1018/750/1600',
|
|
||||||
interactionTip: '点击文创物品,点亮文化福灯',
|
interactionTip: '点击文创物品,点亮文化福灯',
|
||||||
collectedItem: '文化福灯'
|
collectedItem: '文化福灯'
|
||||||
},
|
},
|
||||||
|
|
@ -75,7 +73,6 @@ const scenes = ref([
|
||||||
id: 'wangfujing',
|
id: 'wangfujing',
|
||||||
name: '王府井商圈',
|
name: '王府井商圈',
|
||||||
description: '北京最繁华的商业中心之一',
|
description: '北京最繁华的商业中心之一',
|
||||||
image: 'https://picsum.photos/id/1018/750/1600',
|
|
||||||
interactionTip: '双指放大,收集金袋福卡',
|
interactionTip: '双指放大,收集金袋福卡',
|
||||||
collectedItem: '金袋福卡'
|
collectedItem: '金袋福卡'
|
||||||
},
|
},
|
||||||
|
|
@ -83,7 +80,6 @@ const scenes = ref([
|
||||||
id: 'chongwen',
|
id: 'chongwen',
|
||||||
name: '崇文门商圈',
|
name: '崇文门商圈',
|
||||||
description: '融合传统文化与现代商业的活力区域',
|
description: '融合传统文化与现代商业的活力区域',
|
||||||
image: 'https://picsum.photos/id/1016/750/1600',
|
|
||||||
interactionTip: '滑动探索商圈,收集国潮福字',
|
interactionTip: '滑动探索商圈,收集国潮福字',
|
||||||
collectedItem: '国潮福字'
|
collectedItem: '国潮福字'
|
||||||
},
|
},
|
||||||
|
|
@ -91,8 +87,6 @@ const scenes = ref([
|
||||||
id: 'qianmen',
|
id: 'qianmen',
|
||||||
name: '前门商圈',
|
name: '前门商圈',
|
||||||
description: '北京最具历史文化底蕴的商圈之一',
|
description: '北京最具历史文化底蕴的商圈之一',
|
||||||
image: 'https://picsum.photos/id/1015/750/1600',
|
|
||||||
thumbnail: 'https://picsum.photos/id/1015/200/100',
|
|
||||||
interactionTip: '点击京韵大鼓,收集非遗福印',
|
interactionTip: '点击京韵大鼓,收集非遗福印',
|
||||||
collectedItem: '非遗福印'
|
collectedItem: '非遗福印'
|
||||||
},
|
},
|
||||||
|
|
@ -462,8 +456,10 @@ onUnmounted(() => {
|
||||||
scroll-y="true"
|
scroll-y="true"
|
||||||
scroll-with-animation="true"
|
scroll-with-animation="true"
|
||||||
enhanced="true"
|
enhanced="true"
|
||||||
refresher-enabled="false"
|
:refresher-enabled="false"
|
||||||
|
:refresher-triggered="false"
|
||||||
show-scrollbar="false"
|
show-scrollbar="false"
|
||||||
|
:bounce="false"
|
||||||
@scroll="handleScroll"
|
@scroll="handleScroll"
|
||||||
:scroll-into-view="homeSectionId"
|
:scroll-into-view="homeSectionId"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue