修改用户默认头像为用户名前两位,增加粘贴图片功能,更改充值默认金额为30,优化了一些方法名称

pull/361/head
HXY 2 years ago
parent 485584c011
commit 74692b05b3

@ -24,7 +24,7 @@ type OssCreateService interface {
PersistObject(objectKey string) error PersistObject(objectKey string) error
} }
// OssCreateService Object Storage System Object Delete service // OssDeleteService Object Storage System Object Delete service
type OssDeleteService interface { type OssDeleteService interface {
DeleteObject(objectKey string) error DeleteObject(objectKey string) error
DeleteObjects(objectKeys []string) error DeleteObjects(objectKeys []string) error

@ -62,6 +62,7 @@ func (s *localossCreateServant) PutObject(objectKey string, reader io.Reader, ob
return "", err return "", err
} }
if written != objectSize { if written != objectSize {
fmt.Print("这里发生了错误.......")
os.Remove(savePath) os.Remove(savePath)
return "", errors.New("put object not complete") return "", errors.New("put object not complete")
} }

@ -10,6 +10,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/rocboss/paopao-ce/internal/core"
"image/color" "image/color"
"image/png" "image/png"
"io/ioutil" "io/ioutil"
@ -45,6 +46,8 @@ const (
type pubSrv struct { type pubSrv struct {
api.UnimplementedPubServant api.UnimplementedPubServant
*base.DaoServant *base.DaoServant
oss core.ObjectStorageService
} }
type ThirdPartyUserDataVo struct { type ThirdPartyUserDataVo struct {
@ -214,12 +217,17 @@ func (s *pubSrv) Register(req *web.RegisterReq) (*web.RegisterResp, mir.Error) {
logrus.Errorf("scheckPassword err: %v", err) logrus.Errorf("scheckPassword err: %v", err)
return nil, web.ErrUserRegisterFailed return nil, web.ErrUserRegisterFailed
} }
avatarURL := s.getRandomAvatarByUsername(req.Username)
if len(avatarURL) == 0 {
fmt.Println("头像生成失败或为空")
}
password, salt := encryptPasswordAndSalt(req.Password) password, salt := encryptPasswordAndSalt(req.Password)
user := &ms.User{ user := &ms.User{
Nickname: req.Username, Nickname: req.Username,
Username: req.Username, Username: req.Username,
Password: password, Password: password,
Avatar: getRandomAvatar(), Avatar: avatarURL,
Salt: salt, Salt: salt,
Status: ms.UserStatusNormal, Status: ms.UserStatusNormal,
} }
@ -314,8 +322,9 @@ func (s *pubSrv) validUsername(username string) mir.Error {
return nil return nil
} }
func newPubSrv(s *base.DaoServant) api.Pub { func newPubSrv(s *base.DaoServant, oss core.ObjectStorageService) api.Pub {
return &pubSrv{ return &pubSrv{
DaoServant: s, DaoServant: s,
oss: oss,
} }
} }

@ -5,7 +5,19 @@
package web package web
import ( import (
"bytes"
"fmt"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"hash/fnv"
"image" "image"
"image/color"
"image/draw"
"image/png"
"io"
"io/ioutil"
"log"
"math/rand" "math/rand"
"strings" "strings"
"time" "time"
@ -79,6 +91,122 @@ func getRandomAvatar() string {
return defaultAvatars[rand.Intn(len(defaultAvatars))] return defaultAvatars[rand.Intn(len(defaultAvatars))]
} }
func (s *pubSrv) getRandomAvatarByUsername(username string) string {
// 获取前两个字
runes := []rune(username)
showName := string(runes[:2])
//转换为大写
showName = strings.ToUpper(showName)
// 获取随机颜色作为头像底色
finalColor := getRandomColor(username)
//生成随机头像文件
// 生成头像图片
imageSize := 50 // 设置头像图片大小
avatar := createAvatar(imageSize, finalColor, showName)
//avatarFilename := username + ".png"
// 保存头像图片到文件
avatarBuffer := &bytes.Buffer{}
err := saveImageToBuffer(avatar, avatarBuffer)
if err != nil {
fmt.Println("保存头像到缓冲区失败:", err)
return "注册失败"
}
if avatarBuffer.Len() == 0 {
fmt.Println("头像缓冲区为空")
return "注册失败"
}
// 生成随机路径
randomPath := uuid.Must(uuid.NewV4()).String()
ossSavePath := "public/avatar/" + generatePath(randomPath[:8]) + "/" + randomPath[9:] + ".png"
if ossSavePath == "" {
fmt.Println("ossSavePath为空")
return "注册失败"
}
//fmt.Print(ossSavePath + "\n" + avatarBuffer.String() + "\n" + strconv.Itoa(imageSize) + "\n" + "image/png" + "\n" + "false")
objectUrl, err := s.oss.PutObject(ossSavePath, avatarBuffer, int64(avatarBuffer.Len()), "image/png", false)
if err != nil {
logrus.Errorf("oss.PutObject err: %s", err)
return "失败"
}
fmt.Print(objectUrl)
return objectUrl
}
// 生成随机颜色
func getRandomColor(username string) color.RGBA {
hasher := fnv.New32()
hasher.Write([]byte(username))
hash := hasher.Sum32()
return color.RGBA{
R: 150 + uint8(hash>>16)%106,
G: 150 + uint8((hash>>8)%106),
B: 150 + uint8(hash%106),
A: 255,
}
}
// 创建头像
func createAvatar(size int, bgColor color.RGBA, text string) image.Image {
avatar := image.NewRGBA(image.Rect(0, 0, size, size))
draw.Draw(avatar, avatar.Bounds(), &image.Uniform{bgColor}, image.Point{}, draw.Src)
// 在头像上绘制文字
textColor := color.RGBA{255, 255, 255, 255} // 白色文字
fontBytes, err := ioutil.ReadFile("internal/servants/web/typeface/TTF/SourceSans3-Black.ttf")
if err != nil {
log.Println("unable to read font file:", err)
return avatar
}
fontParsed, err := truetype.Parse(fontBytes)
if err != nil {
log.Println("unable to parse font file:", err)
return avatar
}
// 使用 freetype 作为字体渲染器
c := freetype.NewContext()
c.SetDPI(72)
c.SetFont(fontParsed)
c.SetFontSize(float64(size / 2)) // 设置字体大小
c.SetClip(avatar.Bounds())
c.SetDst(avatar)
c.SetSrc(image.NewUniform(textColor))
c.SetHinting(font.HintingFull)
// 创建字体面以计算字符串大小
opts := truetype.Options{}
opts.Size = float64(size) / 2
face := truetype.NewFace(fontParsed, &opts)
// 获取文本的宽度和高度
textWidth := font.MeasureString(face, text).Round()
textHeight := face.Metrics().Height.Round()
// 计算绘制文本的起点,以使其居中
x := (size - textWidth) / 2
y := (size + textHeight/2) / 2
pt := freetype.Pt(x, y)
_, err = c.DrawString(text, pt)
if err != nil {
log.Println("failed to draw string:", err)
}
return avatar
}
func saveImageToBuffer(img image.Image, buffer io.Writer) error {
return png.Encode(buffer, img)
}
// checkPassword 密码检查 // checkPassword 密码检查
func checkPassword(password string) mir.Error { func checkPassword(password string) mir.Error {
// 检测用户是否合规 // 检测用户是否合规

@ -31,7 +31,7 @@ func RouteWeb(e *gin.Engine) {
api.RegisterCoreServant(e, newCoreSrv(ds, oss)) api.RegisterCoreServant(e, newCoreSrv(ds, oss))
api.RegisterLooseServant(e, newLooseSrv(ds)) api.RegisterLooseServant(e, newLooseSrv(ds))
api.RegisterPrivServant(e, newPrivSrv(ds, oss)) api.RegisterPrivServant(e, newPrivSrv(ds, oss))
api.RegisterPubServant(e, newPubSrv(ds)) api.RegisterPubServant(e, newPubSrv(ds, oss))
api.RegisterKeyQueryServant(e, NewShareKeyServant(ds)) api.RegisterKeyQueryServant(e, NewShareKeyServant(ds))
api.RegisterRankServant(e, NewRankServant(ds)) api.RegisterRankServant(e, NewRankServant(ds))
api.RegisterFollowshipServant(e, newFollowshipSrv(ds)) api.RegisterFollowshipServant(e, newFollowshipSrv(ds))

@ -304,3 +304,19 @@ export const unfollowTopic = (
data, data,
}); });
}; };
export const uploadImage = (
data: NetParams.UploadImageReq,
uploadToken: string // 添加一个参数来传递 Authorization 头的值
): Promise<NetReq.UploadImageResp> => {
return request({
method: 'post',
url: '/v1/attachment',
data,
headers: {
'Authorization': uploadToken, // 设置 Authorization 头,使用传递的 uploadToken
'Content-Type': 'multipart/form-data', // 设置请求头,表明发送的是 FormData
},
});
};

File diff suppressed because it is too large Load Diff

@ -6,7 +6,7 @@
name: 'user', name: 'user',
query: { s: props.reply.user.username }, query: { s: props.reply.user.username },
}"> }">
{{ props.reply.user.username }} {{ props.reply.user.nickname }}
</router-link> </router-link>
<span class="reply-name"> <span class="reply-name">
{{ props.reply.at_user_id > 0 ? '' : ':' }} {{ props.reply.at_user_id > 0 ? '' : ':' }}
@ -16,7 +16,7 @@
name: 'user', name: 'user',
query: { s: props.reply.at_user.username }, query: { s: props.reply.at_user.username },
}" v-if="props.reply.at_user_id > 0"> }" v-if="props.reply.at_user_id > 0">
{{ props.reply.at_user.username }} {{ props.reply.at_user.nickname }}
</router-link> </router-link>
</div> </div>
<div class="timestamp"> <div class="timestamp">

@ -160,6 +160,7 @@ import { useRouter } from "vue-router";
import { getDownloadRank, getHighQuailty, getTags } from "@/api/post"; import { getDownloadRank, getHighQuailty, getTags } from "@/api/post";
import { Search } from "@vicons/ionicons5"; import { Search } from "@vicons/ionicons5";
import { ChevronForward } from "@vicons/ionicons5"; import { ChevronForward } from "@vicons/ionicons5";
import { Ref } from 'vue';
const hotTags = ref<Item.TagProps[]>([]); const hotTags = ref<Item.TagProps[]>([]);
const followTags = ref<Item.TagProps[]>([]); const followTags = ref<Item.TagProps[]>([]);
@ -187,7 +188,7 @@ const DownloadPreWeekRankingList = ref<Item.RankingDataProps[]>([]);
const DownloadPreMonthRankingList = ref<Item.RankingDataProps[]>([]); const DownloadPreMonthRankingList = ref<Item.RankingDataProps[]>([]);
//获取排行榜数据 //获取排行榜数据
const locadHeighQuailtyRankingList = () => { const loadHeighQuailtyRankingList = () => {
rankloading.value = true; rankloading.value = true;
getHighQuailty() getHighQuailty()
.then((res) => { .then((res) => {
@ -199,13 +200,13 @@ const locadHeighQuailtyRankingList = () => {
}); });
}; };
const loadDowmloadRankingByType = (type: number) => { const loadDownloadRankingByType = (type: number) => {
rankloading.value = true; rankloading.value = true;
getDownloadRank(type) getDownloadRank(type)
.then((res) => { .then((res) => {
if (type ===1 ) allDownloadRankingList.value = res.list; if (type === 1) allDownloadRankingList.value = res.list;
if (type === 2) DownloadPreWeekRankingList.value = res.list; else if (type === 2) DownloadPreWeekRankingList.value = res.list;
if (type === 3) DownloadPreMonthRankingList.value = res.list; else if (type === 3) DownloadPreMonthRankingList.value = res.list;
rankloading.value = false; rankloading.value = false;
}) })
.catch((err) => { .catch((err) => {
@ -231,25 +232,28 @@ const toggleRankingType = () => {
NextRankingType.value = rankingTypes[(currentRankingTypeIndex + 1) % rankingTypes.length]; NextRankingType.value = rankingTypes[(currentRankingTypeIndex + 1) % rankingTypes.length];
}; };
//1总 2周 3月 const rankingTypeToFunctionMap: { [key: string]: Ref<Item.RankingDataProps[]> } = {
highQuality: rankingList,
downloadAll: allDownloadRankingList,
downloadPreWeek: DownloadPreWeekRankingList,
downloadPreMonth: DownloadPreMonthRankingList
};
const rankingTypeToLoadFunctionMap: { [key: string]: () => void } = {
downloadAll: () => loadDownloadRankingByType(1),
downloadPreWeek: () => loadDownloadRankingByType(2),
downloadPreMonth: () => loadDownloadRankingByType(3)
};
const getCurrentRankingList = computed(() => { const getCurrentRankingList = computed(() => {
if (currentRankingType.value === "highQuality") { const currentType = currentRankingType.value;
return rankingList.value; const rankingValue = rankingTypeToFunctionMap[currentType as keyof typeof rankingTypeToFunctionMap]?.value;
} else if (currentRankingType.value === "downloadAll") {
if (allDownloadRankingList.value.length === 0) { if (rankingValue !== undefined) {
loadDowmloadRankingByType(1); if (rankingValue.length === 0 && rankingTypeToLoadFunctionMap[currentType]) {
} rankingTypeToLoadFunctionMap[currentType]();
return allDownloadRankingList.value;
} else if (currentRankingType.value === "downloadPreWeek") {
if (DownloadPreWeekRankingList.value.length === 0) {
loadDowmloadRankingByType(2);
}
return DownloadPreWeekRankingList.value;
} else if (currentRankingType.value === "downloadPreMonth") {
if (DownloadPreMonthRankingList.value.length === 0) {
loadDowmloadRankingByType(3);
} }
return DownloadPreMonthRankingList.value; return rankingValue;
} }
return []; return [];
}); });
@ -320,7 +324,7 @@ watch(
); );
onMounted(() => { onMounted(() => {
loadHotTags(); loadHotTags();
locadHeighQuailtyRankingList(); loadHeighQuailtyRankingList();
}); });
</script> </script>

@ -279,4 +279,11 @@ declare module NetParams {
interface PostUnfollowTopic { interface PostUnfollowTopic {
topic_id: number; topic_id: number;
} }
/** 上传图片请求参数 */
interface UploadImageReq {
file: File;
type: string;
}
} }

@ -203,4 +203,15 @@ declare module NetReq {
interface PostFollowTopic {} interface PostFollowTopic {}
interface PostUnfollowTopic {} interface PostUnfollowTopic {}
/** 上传图片响应参数 */
interface UploadImageResp {
/** 图片URL */
content: string;
file_size: number;
img_height: number;
img_width: number;
type: number;
user_id: number;
}
} }

@ -158,7 +158,7 @@ import MainNav from "@/components/main-nav.vue";
const store = useStore(); const store = useStore();
const route = useRoute(); const route = useRoute();
const showRecharge = ref(false); const showRecharge = ref(false);
const selectedRechargeAmount = ref(100); const selectedRechargeAmount = ref(3000);
const recharging = ref(false); const recharging = ref(false);
const rechargeQrcode = ref(''); const rechargeQrcode = ref('');
const loading = ref(false); const loading = ref(false);

Loading…
Cancel
Save