feat: 解决合并冲突

This commit is contained in:
zheng020 2026-06-04 00:03:40 +08:00
parent bc3d82e20e
commit f0b650de26
10 changed files with 667 additions and 663 deletions

3
.gitignore vendored
View File

@ -23,7 +23,6 @@ node_modules
package-lock.json
# Added by code-review-graph
.code-review-graph/
<<<<<<< HEAD
.claude
# 本地重复脚本(不提交仓库)
@ -39,8 +38,6 @@ backend/**/*.exe
# 浏览器远程调试临时文件
.tmp-browser/
=======
.claude/
hookify.*.local.md
.mcp.json
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8

View File

@ -8,7 +8,6 @@ import (
// Config 网关配置
type Config struct {
<<<<<<< HEAD
Server ServerConfig
Dubbo DubboConfig
JWT JWTConfig
@ -37,7 +36,6 @@ type LaserCompositorConfig struct {
type DifyConfig struct {
APIBase string
APIKey string
=======
Server ServerConfig
Dubbo DubboConfig
JWT JWTConfig
@ -45,7 +43,6 @@ type DifyConfig struct {
Redis RedisConfig
WebSocket WebSocketConfig
Root string
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
}
// RedisConfig Redis 配置
@ -178,19 +175,8 @@ func Load() *Config {
Password: getEnv("REDIS_PASSWORD", ""),
DB: getEnvInt("REDIS_DB", 0),
},
<<<<<<< HEAD
DB: DBConfig{
Host: getEnv("DB_HOST", "localhost"),
Port: getEnvInt("DB_PORT", 5432),
User: getEnv("DB_USER", "postgres"),
Password: getEnv("DB_PASSWORD", ""),
DBName: getEnv("DB_NAME", "top-fans"),
SSLMode: getEnv("DB_SSLMODE", "disable"),
TimeZone: getEnv("DB_TIMEZONE", "Asia/Shanghai"),
=======
WebSocket: WebSocketConfig{
AIChatPath: getEnv("WS_AI_CHAT_PATH", "/ws/ai-chat"),
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
},
}
}

View File

@ -86,16 +86,13 @@ func SetupRouter(userClient *client.Client, socialClient *client.Client, assetCl
return nil, err
}
<<<<<<< HEAD
segmentCtrl := controller.NewSegmentController()
laserGenCtrl := controller.NewLaserGenerateController(config.Load())
=======
aiChatCtrl, err := controller.NewAIChatController(aiChatClient)
if err != nil {
return nil, err
}
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
// API v1 路由组
v1 := r.Group("/api/v1")

View File

@ -318,12 +318,9 @@ services:
DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20005
DUBBO_TASK_SERVICE_URL: tri://taskservice:20006
DUBBO_STARBOOK_SERVICE_URL: tri://starbookservice:20007
<<<<<<< HEAD
DIFY_API_BASE: ${DIFY_API_BASE:-http://host.docker.internal:8081/v1}
DIFY_API_KEY: ${DIFY_API_KEY:-}
=======
DUBBO_AI_CHAT_SERVICE_URL: tri://aichatservice:20008
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
depends_on:
userservice:
condition: service_healthy

View File

@ -446,12 +446,9 @@ services:
DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20004
DUBBO_TASK_SERVICE_URL: tri://taskservice:20006
DUBBO_STARBOOK_SERVICE_URL: tri://starbookservice:20005
<<<<<<< HEAD
DIFY_API_BASE: ${DIFY_API_BASE:-http://dify-api:8081/v1}
DIFY_API_KEY: ${DIFY_API_KEY:-}
=======
DUBBO_AI_CHAT_SERVICE_URL: tri://aichatservice:20008
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
REDIS_HOST: topfans-redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-123456}

View File

@ -97,7 +97,6 @@ export default {
this.applyType(val);
},
},
<<<<<<< HEAD
computed: {
currentCardList() {
const categoryName = this.categoryList[this.selectedCategoryIndex].name
@ -240,21 +239,6 @@ export default {
}
}
=======
},
created() {
// type, mall.vue onLoad initialType
// "" watch
this.applyType(this.type);
},
onLoad(options) {
// ,()
this.showMenu = false;
// Tab(square.vue / CastloveContent.vue) type,
// type :star_card | badge | poster
if (options && options.type) {
this.applyType(options.type);
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
}
},
onShow() {

View File

@ -26,7 +26,7 @@
src="/static/dashboard/crystal-bg.png"
mode="aspectFit"
/>
<text class="tab-title">水晶相关</text>
<text class="tab-title">收益总览</text>
</view>
<view
:class="['tab', activeTab === 'season' ? 'tab-active' : '']"

View File

@ -1,11 +1,23 @@
<template>
<view class="register-container">
<!-- 背景图片层 -->
<image class="background-image" src="/static/background/login-bg.png" mode="aspectFill"></image>
<image
class="background-image"
src="/static/background/login-bg.png"
mode="aspectFill"
></image>
<!-- 女孩图片 -->
<image class="girl-image" src="/static/login/person-photo.png" mode="aspectFit"></image>
<image
class="girl-image"
src="/static/login/person-photo.png"
mode="aspectFit"
></image>
<!-- 粉色玩偶遮挡女孩脸的下半部分 -->
<image class="doll-image" src="/static/login/login-character.png" mode="aspectFit"></image>
<image
class="doll-image"
src="/static/login/login-character.png"
mode="aspectFit"
></image>
<view class="background-overlay"></view>
<!-- 内容区域 -->
@ -43,30 +55,40 @@
/>
<view
class="send-code-btn"
:class="{ 'countdown': codeStatus === 'countdown', 'verified': codeStatus === 'verified' }"
:class="{
countdown: codeStatus === 'countdown',
verified: codeStatus === 'verified',
}"
@click="handleSendCode"
>
<text v-if="codeStatus === 'unsent' || codeStatus === 'resend'">发送验证码</text>
<text v-else-if="codeStatus === 'countdown'">{{countdown}}</text>
<text v-if="codeStatus === 'unsent' || codeStatus === 'resend'"
>发送验证码</text
>
<text v-else-if="codeStatus === 'countdown'"
>{{ countdown }}</text
>
<text v-else-if="codeStatus === 'verified'">已验证</text>
</view>
</view>
<!-- 验证码错误提示 -->
<view v-if="codeError" class="error-message">
<text>{{codeError}}</text>
<text>{{ codeError }}</text>
</view>
<!-- 验证按钮验证码输入后显示 -->
<view v-if="showCodeInput && codeStatus !== 'verified'" class="verify-btn-wrapper">
<!-- <view
v-if="showCodeInput && codeStatus !== 'verified'"
class="verify-btn-wrapper"
>
<button
class="btn-secondary"
:disabled="isVerifying || !form.code || form.code.length !== 6"
@click="handleVerifyCode"
>
{{ isVerifying ? '验证中...' : '验证验证码' }}
{{ isVerifying ? "验证中..." : "验证验证码" }}
</button>
</view>
</view> -->
<!-- 密码输入框 -->
<view class="input-wrapper password-wrapper">
@ -78,7 +100,7 @@
placeholder-class="input-placeholder"
/>
<view class="eye-icon" @click="togglePassword">
<text class="eye-text">{{ showPassword ? '👁️' : '👁️‍🗨️' }}</text>
<text class="eye-text">{{ showPassword ? "👁️" : "👁️‍🗨️" }}</text>
</view>
</view>
@ -101,12 +123,14 @@
<view class="footer-text">
<view class="agreement-wrapper">
<view class="agreement-checkbox" @click="toggleAgreement">
<view class="custom-checkbox" :class="{ 'checked': agreedToTerms }">
<view class="custom-checkbox" :class="{ checked: agreedToTerms }">
<view v-if="agreedToTerms" class="checkbox-inner"></view>
</view>
<text class="agreement-label">我已阅读并同意</text>
</view>
<text class="agreement-link" @click="showAgreementModal">Topfans用户服务协议</text>
<text class="agreement-link" @click="showAgreementModal"
>Topfans用户服务协议</text
>
</view>
</view>
</view>
@ -120,23 +144,35 @@
</view>
<view class="tip-dialog-content">
<text class="tip-text">阅读并同意以下条款</text>
<text class="tip-agreement-link" @click="openAgreementFromTip">Topfans用户服务协议</text>
<text class="tip-agreement-link" @click="openAgreementFromTip"
>Topfans用户服务协议</text
>
</view>
</view>
</view>
<!-- 协议全文弹窗 -->
<view v-if="showAgreementDialog" class="agreement-dialog-mask" @click="closeAgreementDialog">
<view
v-if="showAgreementDialog"
class="agreement-dialog-mask"
@click="closeAgreementDialog"
>
<view class="agreement-dialog" @click.stop>
<view class="agreement-dialog-header">
<text class="agreement-dialog-title">Topfans用户服务协议</text>
<text class="agreement-dialog-close" @click="closeAgreementDialog">×</text>
<text class="agreement-dialog-close" @click="closeAgreementDialog"
>×</text
>
</view>
<scroll-view class="agreement-dialog-content" scroll-y>
<text class="agreement-text">{{ agreementContent || '协议内容加载中...' }}</text>
<text class="agreement-text">{{
agreementContent || "协议内容加载中..."
}}</text>
</scroll-view>
<view class="agreement-dialog-footer">
<button class="agreement-confirm-btn" @click="agreeAndClose">我同意</button>
<button class="agreement-confirm-btn" @click="agreeAndClose">
我同意
</button>
</view>
</view>
</view>
@ -144,29 +180,29 @@
</template>
<script setup>
import { ref } from 'vue';
import { validatePhone, validatePassword } from '@/utils/validator';
import { AGREEMENT_CONTENT } from '@/utils/agreement';
import { checkmobileApi, sendCodeApi, verifyCodeApi } from '@/utils/api';
import { ref } from "vue";
import { validatePhone, validatePassword } from "@/utils/validator";
import { AGREEMENT_CONTENT } from "@/utils/agreement";
import { checkmobileApi, sendCodeApi, verifyCodeApi } from "@/utils/api";
//
const form = ref({
phone: '',
password: '',
code: ''
phone: "",
password: "",
code: "",
});
const showPassword = ref(false);
const errorMessage = ref('');
const errorMessage = ref("");
const agreedToTerms = ref(false);
const agreementContent = ref('');
const agreementContent = ref("");
const showAgreementDialog = ref(false);
const showTipDialog = ref(false);
const phoneChecking = ref(false);
const showCodeInput = ref(true); //
const codeStatus = ref('unsent'); // unsent, countdown, resend, verified
const codeStatus = ref("unsent"); // unsent, countdown, resend, verified
const countdown = ref(60);
const codeError = ref('');
const verifyToken = ref('');
const codeError = ref("");
const verifyToken = ref("");
const countdownTimer = ref(null);
const isVerifying = ref(false);
@ -198,7 +234,7 @@ const checkPhoneDuplicate = async (phone) => {
//
const phoneValidation = validatePhone(phone);
if (!phoneValidation.valid || phone.length !== 11) {
errorMessage.value = '';
errorMessage.value = "";
return;
}
@ -208,13 +244,13 @@ const checkPhoneDuplicate = async (phone) => {
try {
const res = await checkmobileApi(phone);
if (res.code === 200 && res.data && res.data.exists) {
errorMessage.value = '该手机号已被注册';
errorMessage.value = "该手机号已被注册";
} else {
errorMessage.value = '';
errorMessage.value = "";
}
} catch (error) {
//
errorMessage.value = '';
errorMessage.value = "";
} finally {
phoneChecking.value = false;
}
@ -230,16 +266,16 @@ const handleSendCode = async () => {
}
try {
const res = await sendCodeApi(form.value.phone, 'register');
const res = await sendCodeApi(form.value.phone, "register");
if (res.code === 200) {
codeStatus.value = 'countdown';
codeStatus.value = "countdown";
countdown.value = res.expires_in || 60;
showCodeInput.value = true;
startCountdown();
uni.showToast({ title: '验证码已发送', icon: 'success' });
uni.showToast({ title: "验证码已发送", icon: "success" });
}
} catch (error) {
codeError.value = error.message || '发送失败,请重试';
codeError.value = error.message || "发送失败,请重试";
}
};
@ -252,7 +288,7 @@ const startCountdown = () => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(countdownTimer.value);
codeStatus.value = 'resend';
codeStatus.value = "resend";
}
}, 1000);
};
@ -260,21 +296,25 @@ const startCountdown = () => {
//
const handleVerifyCode = async () => {
if (!form.value.code || form.value.code.length !== 6) {
codeError.value = '请输入6位验证码';
codeError.value = "请输入6位验证码";
return;
}
isVerifying.value = true;
codeError.value = '';
codeError.value = "";
try {
const res = await verifyCodeApi(form.value.phone, form.value.code, 'register');
const res = await verifyCodeApi(
form.value.phone,
form.value.code,
"register",
);
if (res.code === 200 && res.data && res.data.verified) {
verifyToken.value = res.data.verify_token;
codeStatus.value = 'verified';
uni.showToast({ title: '验证成功', icon: 'success' });
codeStatus.value = "verified";
uni.showToast({ title: "验证成功", icon: "success" });
}
} catch (error) {
codeError.value = error.message || '验证失败';
codeError.value = error.message || "验证失败";
} finally {
isVerifying.value = false;
}
@ -289,7 +329,7 @@ const goToLogin = () => {
} else {
// square
uni.reLaunch({
url: '/pages/login/login'
url: "/pages/login/login",
});
}
};
@ -341,6 +381,7 @@ const openAgreementFromTip = () => {
//
const handleRegister = async () => {
handleVerifyCode();
//
if (!agreedToTerms.value) {
openTipDialog();
@ -348,8 +389,8 @@ const handleRegister = async () => {
}
//
if (showCodeInput.value && codeStatus.value !== 'verified') {
errorMessage.value = '请先完成手机号验证';
if (showCodeInput.value && codeStatus.value !== "verified") {
errorMessage.value = "请先完成手机号验证";
return;
}
@ -359,7 +400,7 @@ const handleRegister = async () => {
errorMessage.value = phoneValidation.message;
uni.showToast({
title: phoneValidation.message,
icon: 'none'
icon: "none",
});
return;
}
@ -369,30 +410,30 @@ const handleRegister = async () => {
errorMessage.value = passwordValidation.message;
uni.showToast({
title: passwordValidation.message,
icon: 'none'
icon: "none",
});
return;
}
errorMessage.value = '';
errorMessage.value = "";
try {
// 使
uni.setStorageSync('temp_register_mobile', form.value.phone);
uni.setStorageSync('temp_register_password', form.value.password);
uni.setStorageSync("temp_register_mobile", form.value.phone);
uni.setStorageSync("temp_register_password", form.value.password);
// verify_token
uni.setStorageSync('temp_register_verify_token', verifyToken.value);
uni.setStorageSync("temp_register_verify_token", verifyToken.value);
//
uni.reLaunch({
url: '/pages/profile/setNickname'
url: "/pages/profile/setNickname",
});
} catch (error) {
errorMessage.value = error.message || '操作失败,请重试';
errorMessage.value = error.message || "操作失败,请重试";
uni.showToast({
title: errorMessage.value,
icon: 'none'
icon: "none",
});
}
};
@ -489,15 +530,15 @@ const handleRegister = async () => {
font-size: 135rpx;
font-weight: 600;
letter-spacing: 12rpx;
background: linear-gradient(to right, #B52920 0%, #86D9E0 85%, #56C1FF 110%);
background: linear-gradient(to right, #b52920 0%, #86d9e0 85%, #56c1ff 110%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: #8B0000;
color: #8b0000;
display: inline-block;
line-height: 1.2;
text-transform: uppercase;
font-family: 'TheMiladiatorRegular', sans-serif !important;
font-family: "TheMiladiatorRegular", sans-serif !important;
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.15));
display: flex;
justify-content: center;
@ -547,7 +588,7 @@ const handleRegister = async () => {
}
.send-code-btn.verified {
background: #4CAF50;
background: #4caf50;
}
.verify-btn-wrapper {
@ -558,11 +599,11 @@ const handleRegister = async () => {
width: 100%;
height: 88rpx;
background: #fff;
border: 2rpx solid #B94E73;
border: 2rpx solid #b94e73;
border-radius: 50rpx;
font-size: 32rpx;
font-weight: bold;
color: #B94E73;
color: #b94e73;
display: flex;
align-items: center;
justify-content: center;
@ -614,7 +655,13 @@ const handleRegister = async () => {
width: 100%;
height: 100rpx;
margin-bottom: 30rpx;
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
background: linear-gradient(
165deg,
#f0e4b1 0%,
#f08399 50%,
#b94e73 90%,
#834b9e 100%
);
border-radius: 50rpx;
border: none;
font-size: 36rpx;
@ -884,10 +931,9 @@ const handleRegister = async () => {
.tip-agreement-link {
font-size: 32rpx;
color: #007AFF;
color: #007aff;
text-decoration: underline;
cursor: pointer;
display: block;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 1.6 MiB