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

View File

@ -8,7 +8,6 @@ import (
// Config 网关配置 // Config 网关配置
type Config struct { type Config struct {
<<<<<<< HEAD
Server ServerConfig Server ServerConfig
Dubbo DubboConfig Dubbo DubboConfig
JWT JWTConfig JWT JWTConfig
@ -37,7 +36,6 @@ type LaserCompositorConfig struct {
type DifyConfig struct { type DifyConfig struct {
APIBase string APIBase string
APIKey string APIKey string
=======
Server ServerConfig Server ServerConfig
Dubbo DubboConfig Dubbo DubboConfig
JWT JWTConfig JWT JWTConfig
@ -45,7 +43,6 @@ type DifyConfig struct {
Redis RedisConfig Redis RedisConfig
WebSocket WebSocketConfig WebSocket WebSocketConfig
Root string Root string
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
} }
// RedisConfig Redis 配置 // RedisConfig Redis 配置
@ -178,19 +175,8 @@ func Load() *Config {
Password: getEnv("REDIS_PASSWORD", ""), Password: getEnv("REDIS_PASSWORD", ""),
DB: getEnvInt("REDIS_DB", 0), 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{ WebSocket: WebSocketConfig{
AIChatPath: getEnv("WS_AI_CHAT_PATH", "/ws/ai-chat"), 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 return nil, err
} }
<<<<<<< HEAD
segmentCtrl := controller.NewSegmentController() segmentCtrl := controller.NewSegmentController()
laserGenCtrl := controller.NewLaserGenerateController(config.Load()) laserGenCtrl := controller.NewLaserGenerateController(config.Load())
=======
aiChatCtrl, err := controller.NewAIChatController(aiChatClient) aiChatCtrl, err := controller.NewAIChatController(aiChatClient)
if err != nil { if err != nil {
return nil, err return nil, err
} }
>>>>>>> 0284bd6951fafc8a3862644ede979dc44eb6efe8
// API v1 路由组 // API v1 路由组
v1 := r.Group("/api/v1") v1 := r.Group("/api/v1")

View File

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

View File

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

View File

@ -97,7 +97,6 @@ export default {
this.applyType(val); this.applyType(val);
}, },
}, },
<<<<<<< HEAD
computed: { computed: {
currentCardList() { currentCardList() {
const categoryName = this.categoryList[this.selectedCategoryIndex].name 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() { onShow() {

View File

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

View File

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